souffle 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,70 @@
1
+ module Souffle
2
+ class Node; end
3
+ end
4
+
5
+ require 'souffle/node/runlist_item'
6
+ require 'souffle/node/runlist'
7
+
8
+ module Souffle
9
+ # A node object that's part of a given system.
10
+ class Node
11
+ attr_accessor :dependencies, :run_list, :parent
12
+ attr_reader :children
13
+
14
+ state_machine :state, :initial => :uninitialized do
15
+ end
16
+
17
+ # Creates a new souffle node with bare dependencies and run_list.
18
+ def initialize
19
+ @dependencies = Souffle::Node::RunList.new
20
+ @run_list = Souffle::Node::RunList.new
21
+ @parent = nil
22
+ @children = []
23
+ super() # NOTE: This is here to initialize state_machine.
24
+ end
25
+
26
+ # Check whether or not a given node depends on another node.
27
+ #
28
+ # @param [ Souffle::Node ] node Check to see whether this node depends
29
+ #
30
+ # @return [ true,false ] Whether or not this node depends on the given.
31
+ def depends_on?(node)
32
+ depends = false
33
+ self.dependencies.each do |d|
34
+ if node.run_list.include? d
35
+ depends = true
36
+ end
37
+ end
38
+ depends
39
+ end
40
+
41
+ # Adds a child node to the current node.
42
+ #
43
+ # @param [ Souffle::Node ] node The node to add as a child.
44
+ #
45
+ # @raise [ InvaidChild ] Children must have dependencies and a run_list.
46
+ def add_child(node)
47
+ unless node.respond_to?(:dependencies) && node.respond_to?(:run_list)
48
+ raise Souffle::Exceptions::InvalidChild,
49
+ "Child must act as a Souffle::Node"
50
+ end
51
+ node.parent = self
52
+ @children.push(node)
53
+ end
54
+
55
+ # Iterator method for children.
56
+ #
57
+ # @yield [ Souffle::Node,nil ] The child node.
58
+ def each_child
59
+ @children.each { |child| yield child }
60
+ end
61
+
62
+ # Equality comparator for nodes.
63
+ #
64
+ # @param [ Souffle::Node ] other The node to compare against.
65
+ def eql?(other)
66
+ @dependencies == other.dependencies && @run_list == other.run_list
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,16 @@
1
+ require 'souffle/provider'
2
+
3
+ # The AWS souffle provider.
4
+ class Souffle::Provider::AWS < Souffle::Provider
5
+
6
+ # Setup the internal AWS configuration and object.
7
+ def setup
8
+ end
9
+
10
+ # The name of the given provider.
11
+ def name; "AWS"; end
12
+
13
+ # Creates a raid array with the given requirements.
14
+ def create_raid
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ require 'souffle/provider'
2
+
3
+ # The Vagrant souffle provider.
4
+ class Souffle::Provider::Vagrant < Souffle::Provider
5
+
6
+ # Setup the internal Vagrant configuration and object.
7
+ def setup
8
+ end
9
+
10
+ # The name of the given provider.
11
+ def name; "Vagrant"; end
12
+
13
+ # Noop.
14
+ def create_raid; end
15
+ end
@@ -0,0 +1,27 @@
1
+ # The souffle cloud provider class.
2
+ class Souffle::Provider
3
+
4
+ # The setup method for the provider. Intended to be overridden.
5
+ #
6
+ # @raise [Souffle::Exceptions::Provider] This definition must be overridden.
7
+ def setup
8
+ error_msg = "#{self.to_s}: you must override setup"
9
+ raise Souffle::Exceptions::Provider, error_msg
10
+ end
11
+
12
+ # The name of the given provider. Intended to be overridden.
13
+ #
14
+ # @raise [Souffle::Exceptions::Provider] This definition must be overridden.
15
+ def name
16
+ error_msg = "#{self.to_s}: you must override name"
17
+ raise Souffle::Exceptions::Provider, error_msg
18
+ end
19
+
20
+ # Creates a raid array for a given provider. Intended to be overridden.
21
+ #
22
+ # @raise [Souffle::Exceptions::Provider] This definition must be overridden.
23
+ def create_raid
24
+ error_msg = "#{self.to_s}: you must override create_raid"
25
+ raise Souffle::Exceptions::Provider, error_msg
26
+ end
27
+ end
@@ -0,0 +1,2 @@
1
+ require 'souffle/provider/aws'
2
+ require 'souffle/provider/vagrant'
@@ -0,0 +1,14 @@
1
+ module Souffle
2
+ # The souffle server and management daemon.
3
+ class Server
4
+
5
+ # Creates a new souffle server.
6
+ def initialize
7
+ end
8
+
9
+ # Runs the server.
10
+ def run
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,51 @@
1
+ module Souffle
2
+ # A system description with nodes and the statemachine to manage them.
3
+ class System
4
+ attr_reader :nodes, :root, :provider
5
+
6
+ state_machine :state, :initial => :uninitialized do
7
+ before_transition :uninitialized => any - :uninitialized,
8
+ :do => :initialize_provider
9
+
10
+ around_transition do |system, transition, block|
11
+ start = Time.now
12
+ block.call
13
+ system.time_used += Time.now - start
14
+ end
15
+ end
16
+
17
+ # Creates a new souffle system, defaulting to using Vagrant as a provider.
18
+ #
19
+ # @param [ String ] provider The provider to use for the given system.
20
+ def initialize(provider="Vagrant")
21
+ initialize_provider(provider)
22
+ super() # NOTE: This is here to initialize state_machine.
23
+ end
24
+
25
+ def initialize_provider(provider)
26
+ @provider = Souffle::Provider.const_get(provider.to_sym).new
27
+ rescue
28
+ raise Souffle::Exceptions::InvalidProvider,
29
+ "The provider Souffle::Provider::#{provider} does not exist."
30
+ end
31
+
32
+ # Proxy to the provider setup routine.
33
+ def setup_provider
34
+ @provider.setup
35
+ end
36
+
37
+ # Adds the root node to the system.
38
+ #
39
+ # @param [ Souffle::Node ] node The node to become to root node.
40
+ def root=(node)
41
+ @root = node
42
+ end
43
+
44
+ # Adds a node to the system tree.
45
+ #
46
+ # @param [ Souffle::Node ] node The node to add into the tree.
47
+ def add(node)
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,14 @@
1
+ module Souffle
2
+ # The souffle worker daemon.
3
+ class Worker
4
+
5
+ # Creates a new souffle worker.
6
+ def initialize
7
+ end
8
+
9
+ # Runs the worker.
10
+ def run
11
+ end
12
+
13
+ end
14
+ end
data/lib/souffle.rb ADDED
@@ -0,0 +1,17 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'yajl'
3
+ require 'eventmachine'
4
+ require 'state_machine'
5
+ require 'right_aws'
6
+
7
+ # An orchestrator for setting up isolated chef-managed systems.
8
+ module Souffle
9
+ VERSION = "0.0.1"
10
+ end
11
+
12
+ require 'souffle/log'
13
+ require 'souffle/exceptions'
14
+ require 'souffle/config'
15
+ require 'souffle/providers'
16
+ require 'souffle/node'
17
+ require 'souffle/system'
@@ -0,0 +1,6 @@
1
+ {
2
+ "random_something": 1234,
3
+ "aws_access_key": "test_key",
4
+ "aws_access_secret": "test_secret"
5
+ }
6
+
@@ -0,0 +1,4 @@
1
+ random_something 1234
2
+ aws_access_key "test_key"
3
+ aws_access_secret "test_secret"
4
+
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Souffle::Config" do
4
+ it "should be able to read from a ruby config file" do
5
+ config = File.join(File.dirname(__FILE__), 'config', 'example.rb')
6
+ Souffle::Config.from_file(config)
7
+
8
+ Souffle::Config[:random_something].should == 1234
9
+ Souffle::Config[:aws_access_key].should == "test_key"
10
+ Souffle::Config[:aws_access_secret].should == "test_secret"
11
+ end
12
+
13
+ it "should be able to read from a json config stream" do
14
+ config = File.join(File.dirname(__FILE__), 'config', 'example.json')
15
+ Souffle::Config.from_stream_json(IO.read(config))
16
+
17
+ Souffle::Config[:random_something].should == 1234
18
+ Souffle::Config[:aws_access_key].should == "test_key"
19
+ Souffle::Config[:aws_access_secret].should == "test_secret"
20
+ end
21
+
22
+ it "should be able to read from a json config file" do
23
+ config = File.join(File.dirname(__FILE__), 'config', 'example.json')
24
+ Souffle::Config.from_file(config, "json")
25
+
26
+ Souffle::Config[:random_something].should == 1234
27
+ Souffle::Config[:aws_access_key].should == "test_key"
28
+ Souffle::Config[:aws_access_secret].should == "test_secret"
29
+ end
30
+ end
data/spec/node_spec.rb ADDED
@@ -0,0 +1,87 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Souffle::Node" do
4
+ before(:each) do
5
+ @node = Souffle::Node.new
6
+ end
7
+
8
+ after(:each) do
9
+ @node = nil
10
+ end
11
+
12
+ it "should be able to setup dependencies" do
13
+ @node.dependencies << "recipe[chef_server]"
14
+ item = @node.dependencies.first
15
+ item.type.should eql("recipe")
16
+ item.name.should eql("chef_server")
17
+ end
18
+
19
+ it "should be able to setup the run_list" do
20
+ @node.run_list << "role[example_role]"
21
+ item = @node.run_list.first
22
+ item.type.should eql("role")
23
+ item.name.should eql("example_role")
24
+ end
25
+
26
+ it "should fail on improper run_list type" do
27
+ lambda { @node.run_list<<("b0rken[role]") }.should raise_error
28
+ end
29
+
30
+ it "should fail on improper run_list name" do
31
+ lambda { @node.run_list << "role[GT***]}" }.should raise_error
32
+ end
33
+
34
+ it "should be able to describe a single node" do
35
+ @node.run_list << "recipe[chef_server::rubygems_install]"
36
+ @node.run_list << "role[dns_server]"
37
+ end
38
+
39
+ it "should be able to test whether or not a node depends on another" do
40
+ @node.run_list << "role[dns_server]"
41
+ node2 = Souffle::Node.new
42
+ node2.dependencies << "role[dns_server]"
43
+ node2.depends_on?(@node).should eql(true)
44
+ end
45
+
46
+ it "should not depend on another node when there are no dependencies" do
47
+ @node.run_list << "role[dns_server]"
48
+ node2 = Souffle::Node.new
49
+ node2.depends_on?(@node).should eql(false)
50
+ end
51
+
52
+ it "should be able to add child nodes" do
53
+ child = Souffle::Node.new
54
+ lambda { @node.add_child(child) }.should_not raise_error
55
+ @node.children.should eql([child])
56
+ end
57
+
58
+ it "should raise and error on adding an invalid child" do
59
+ child = []
60
+ lambda { node.add_child(child) }.should raise_error
61
+ end
62
+
63
+ it "should be able to iterate across children" do
64
+ child1 = Souffle::Node.new
65
+ child2 = Souffle::Node.new
66
+ @node.add_child(child1)
67
+ @node.add_child(child2)
68
+
69
+ children = [child1, child2]
70
+ @node.each_child { |c| children.delete(c) }
71
+ children.should eql([])
72
+ end
73
+
74
+ it "should be able to test node equality" do
75
+ @node.dependencies << "role[awesome]"
76
+ @node.run_list << "recipe[the_best]"
77
+
78
+ node2 = Souffle::Node.new
79
+ node2.dependencies << "role[awesome]"
80
+ node2.run_list << "recipe[the_best]"
81
+ @node.should eql(node2)
82
+ end
83
+
84
+ it "should have an initial state of `:uninitialized`" do
85
+ @node.state_name.should eql(:uninitialized)
86
+ end
87
+ end
@@ -0,0 +1,83 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Souffle::Node::RunListParser" do
4
+ it "should be able to check whether a hash name is a valid word" do
5
+ ex = Hash.new
6
+ ex["name"] = "AwesomeWord"
7
+ d = lambda { Souffle::Node::RunListParser.gaurentee_name_is_word(ex) }
8
+ d.should_not raise_error
9
+ end
10
+
11
+ it "should raise an error when the hash name is spaced" do
12
+ ex = Hash.new
13
+ ex["name"] = "AwesomeWord SecondWord"
14
+ d = lambda { Souffle::Node::RunListParser.gaurentee_name_is_word(ex) }
15
+ d.should raise_error
16
+ end
17
+
18
+ it "should raise an error when the hash name contains invalid characters" do
19
+ ex = Hash.new
20
+ ex["name"] = "AwesomeWord**"
21
+ d = lambda { Souffle::Node::RunListParser.gaurentee_name_is_word(ex) }
22
+ d.should raise_error
23
+ end
24
+
25
+ it "should raise an error when the hash is empty" do
26
+ ex = Hash.new
27
+ d = lambda { Souffle::Node::RunListParser.gaurentee_valid_keys(ex) }
28
+ d.should raise_error
29
+ end
30
+
31
+ it "should raise an error when the hash is nil" do
32
+ ex = nil
33
+ d = lambda { Souffle::Node::RunListParser.gaurentee_valid_keys(ex) }
34
+ d.should raise_error
35
+ end
36
+
37
+ it "should raise an error when the name is nil but the type is valid" do
38
+ ex = Hash.new
39
+ ex["name"] = nil
40
+ ex["type"] = "role"
41
+ d = lambda { Souffle::Node::RunListParser.gaurentee_valid_keys(ex) }
42
+ d.should raise_error
43
+ end
44
+
45
+ it "should raise an error when the name is empty but the type is valid" do
46
+ ex = Hash.new
47
+ ex["name"] = ""
48
+ ex["type"] = "recipe"
49
+ d = lambda { Souffle::Node::RunListParser.gaurentee_valid_keys(ex) }
50
+ d.should raise_error
51
+ end
52
+
53
+ it "should raise an error when the name is valid but the type is nil" do
54
+ ex = Hash.new
55
+ ex["name"] = "best_name"
56
+ ex["type"] = nil
57
+ d = lambda { Souffle::Node::RunListParser.gaurentee_valid_keys(ex) }
58
+ d.should raise_error
59
+ end
60
+
61
+ it "should raise an error when the name is valud but the type is empty" do
62
+ ex = Hash.new
63
+ ex["name"] = "anotherone"
64
+ ex["type"] = ""
65
+ d = lambda { Souffle::Node::RunListParser.gaurentee_valid_keys(ex) }
66
+ d.should raise_error
67
+ end
68
+
69
+ it "should raise an error when parsing an invalid type" do
70
+ d = lambda { Souffle::Node::RunListParser.parse("rofsdfsdle[somerole]") }
71
+ d.should raise_error
72
+ end
73
+
74
+ it "should be able to parse a role" do
75
+ r = Souffle::Node::RunListParser.parse("role[painfully_long_role]")
76
+ r.should eql( {"type" => "role", "name" => "painfully_long_role"} )
77
+ end
78
+
79
+ it "should be able to parse a recipe" do
80
+ r = Souffle::Node::RunListParser.parse("recipe[a_pretty_serious_recipe]")
81
+ r.should eql( {"type" => "recipe", "name" => "a_pretty_serious_recipe"} )
82
+ end
83
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Souffle::Node::RunList" do
4
+ it "should be able to add a runlist item onto the runlist" do
5
+ rl = Souffle::Node::RunList.new
6
+ rl << "role[dns_server]"
7
+ rl << "recipe[chef_server::rubygems_install]"
8
+ end
9
+
10
+ it "should raise an error when an invalid runlist item is added" do
11
+ rl = Souffle::Node::RunList.new
12
+ lambda { rl << "fsjklfds" }.should raise_error
13
+ end
14
+ end
@@ -0,0 +1,32 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ if RUBY_VERSION =~ /^1\.9/
5
+ require 'simplecov'
6
+
7
+ module SimpleCov::Configuration
8
+ def clean_filters
9
+ @filters = []
10
+ end
11
+ end
12
+
13
+ SimpleCov.configure do
14
+ clean_filters
15
+ load_adapter 'test_frameworks'
16
+ end
17
+
18
+ ENV["COVERAGE"] && SimpleCov.start do
19
+ add_filter "/.rvm/"
20
+ end
21
+ end
22
+
23
+ require 'rspec'
24
+ require 'souffle'
25
+
26
+ # Requires supporting files with custom matchers and macros, etc,
27
+ # in ./support/ and its subdirectories.
28
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
29
+
30
+ RSpec.configure do |config|
31
+
32
+ end
@@ -0,0 +1,35 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Souffle::System" do
4
+ before(:each) do
5
+ @system = Souffle::System.new
6
+ end
7
+
8
+ after(:each) do
9
+ @system = nil
10
+ end
11
+
12
+ it "should be able to setup a Vagrant provider" do
13
+ @system.provider.name.should eql("Vagrant")
14
+ end
15
+
16
+ it "should be able to setup an AWS provider" do
17
+ @system = Souffle::System.new("AWS")
18
+ @system.provider.name.should eql("AWS")
19
+ end
20
+
21
+ it "should raise an InvalidProvider error when the provider doesn't exist" do
22
+ d = lambda { @system = Souffle::System.new("UnholyProviderOfBadness") }
23
+ d.should raise_error
24
+ end
25
+
26
+ it "should be able to add a root node" do
27
+ node = Souffle::Node.new
28
+ @system.root = node
29
+ @system.root.should eql(node)
30
+ end
31
+
32
+ it "should have an initial state of `:uninitialized`" do
33
+ @system.state_name.should eql(:uninitialized)
34
+ end
35
+ end