souffle 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/Gemfile +9 -3
  2. data/README.md +6 -0
  3. data/bin/{souffle-server → souffle} +0 -0
  4. data/lib/souffle.rb +8 -8
  5. data/lib/souffle/application.rb +15 -10
  6. data/lib/souffle/application/souffle-server.rb +90 -5
  7. data/lib/souffle/config.rb +88 -59
  8. data/lib/souffle/daemon.rb +156 -0
  9. data/lib/souffle/exceptions.rb +29 -17
  10. data/lib/souffle/http.rb +43 -0
  11. data/lib/souffle/log.rb +11 -14
  12. data/lib/souffle/node.rb +91 -53
  13. data/lib/souffle/node/runlist.rb +16 -18
  14. data/lib/souffle/node/runlist_item.rb +43 -36
  15. data/lib/souffle/node/runlist_parser.rb +60 -62
  16. data/lib/souffle/polling_event.rb +110 -0
  17. data/lib/souffle/provider.rb +231 -23
  18. data/lib/souffle/provider/aws.rb +654 -7
  19. data/lib/souffle/provider/vagrant.rb +42 -5
  20. data/lib/souffle/provisioner.rb +55 -0
  21. data/lib/souffle/provisioner/node.rb +157 -0
  22. data/lib/souffle/provisioner/system.rb +195 -0
  23. data/lib/souffle/redis_client.rb +8 -0
  24. data/lib/souffle/redis_mixin.rb +40 -0
  25. data/lib/souffle/server.rb +42 -8
  26. data/lib/souffle/ssh_monkey.rb +8 -0
  27. data/lib/souffle/state.rb +16 -0
  28. data/lib/souffle/system.rb +139 -37
  29. data/lib/souffle/template.rb +30 -0
  30. data/lib/souffle/templates/Vagrantfile.erb +41 -0
  31. data/lib/souffle/version.rb +6 -0
  32. data/spec/config_spec.rb +20 -0
  33. data/spec/log_spec.rb +24 -0
  34. data/spec/{runlist_parser_spec.rb → node/runlist_parser_spec.rb} +1 -1
  35. data/spec/{runlist_spec.rb → node/runlist_spec.rb} +1 -1
  36. data/spec/node_spec.rb +43 -8
  37. data/spec/provider_spec.rb +56 -0
  38. data/spec/providers/aws_provider_spec.rb +114 -0
  39. data/spec/providers/vagrant_provider_spec.rb +22 -0
  40. data/spec/provisioner_spec.rb +47 -0
  41. data/spec/spec_helper.rb +8 -0
  42. data/spec/system_spec.rb +242 -13
  43. data/spec/template_spec.rb +20 -0
  44. data/spec/templates/example_template.erb +1 -0
  45. metadata +125 -30
  46. data/bin/souffle-worker +0 -7
  47. data/lib/souffle/application/souffle-worker.rb +0 -46
  48. data/lib/souffle/providers.rb +0 -2
  49. data/lib/souffle/worker.rb +0 -14
@@ -1,21 +1,33 @@
1
- module Souffle
2
- # Souffle specific exceptions; intended for debugging purposes.
3
- class Exceptions
4
-
5
- # Application level error.
6
- class Application < RuntimeError; end
7
- # Provider level error.
8
- class Provider < RuntimeError; end
1
+ # Souffle specific exceptions; intended for debugging purposes.
2
+ class Souffle::Exceptions
3
+
4
+ # Application level error.
5
+ class Application < RuntimeError; end
6
+ # Provider level error.
7
+ class Provider < RuntimeError; end
9
8
 
10
- # Runlist Name cannot be nil or empty and must be a word [A-Za-z0-9_:].
11
- class InvalidRunlistName < RuntimeError; end
12
- # Runlist Type cannot be nil or empty and must be one of (role|recipe).
13
- class InvalidRunlistType < RuntimeError; end
9
+ # Runlist Name cannot be nil or empty and must be a word [A-Za-z0-9_:].
10
+ class InvalidRunlistName < RuntimeError; end
11
+ # Runlist Type cannot be nil or empty and must be one of (role|recipe).
12
+ class InvalidRunlistType < RuntimeError; end
14
13
 
15
- # Node children must respond to dependencies and run_list.
16
- class InvalidChild < RuntimeError; end
14
+ # Node children must respond to dependencies and run_list.
15
+ class InvalidChild < RuntimeError; end
16
+ # Node parents must respond to dependencies and run_list.
17
+ class InvalidParent < RuntimeError; end
17
18
 
18
- # The provider must exist in souffle/providers.
19
- class InvalidProvider < RuntimeError; end
20
- end
19
+ # The provider must exist in souffle/providers.
20
+ class InvalidProvider < RuntimeError; end
21
+
22
+ # The system hash must have a nodes key with a list of nodes.
23
+ class InvalidSystemHash < RuntimeError; end
24
+
25
+ # The souffle ssh directory must have writable permissions.
26
+ class PermissionErrorSshKeys < RuntimeError; end
27
+
28
+ # The AWS Instance searched for does not exist.
29
+ class AwsInstanceDoesNotExist < RuntimeError; end
30
+
31
+ # The AWS Keys are invalid.
32
+ class InvalidAwsKeys < RuntimeError; end
21
33
  end
@@ -0,0 +1,43 @@
1
+ require 'sinatra/base'
2
+
3
+ require 'souffle/state'
4
+
5
+ # The souffle service REST interface.
6
+ class Souffle::Http < Sinatra::Base
7
+ before { content_type :json }
8
+
9
+ # Returns the current version of souffle.
10
+ get '/' do
11
+ { :name => 'souffle',
12
+ :version => Souffle::VERSION }.to_json
13
+ end
14
+
15
+ # Returns the current status of souffle.
16
+ get '/status' do
17
+ { :status => Souffle::State.status }.to_json
18
+ end
19
+
20
+ # Returns the id for the created environment or false on failure.
21
+ put '/create' do
22
+ begin
23
+ data = JSON.parse(request.body.read, :symbolize_keys => true)
24
+ rescue
25
+ status 415
26
+ return { :success => false,
27
+ :message => "Invalid json in request." }.to_json
28
+ end
29
+
30
+ user = data[:user]
31
+ msg = "Http request to create a new system"
32
+ msg << " for user: #{user}" if user
33
+ Souffle::Log.debug msg
34
+ Souffle::Log.debug data.to_s
35
+
36
+ provider = Souffle::Provider::AWS.new
37
+
38
+ system = Souffle::System.from_hash(data)
39
+ provider.create_system(system)
40
+
41
+ { :success => true }.to_json
42
+ end
43
+ end
data/lib/souffle/log.rb CHANGED
@@ -1,22 +1,19 @@
1
1
  require 'logger'
2
2
  require 'mixlib/log'
3
3
 
4
- module Souffle
5
- # Souffle's internal logging facility.
6
- # Standardized to provide a consistent log format.
7
- class Log
8
- extend Mixlib::Log
4
+ # Souffle's internal logging facility.
5
+ # Standardized to provide a consistent log format.
6
+ class Souffle::Log
7
+ extend Mixlib::Log
9
8
 
10
- # Force initialization of the primary log device (@logger)
11
- init
9
+ # Force initialization of the primary log device (@logger).
10
+ init
12
11
 
13
- # Monkeypatch Formatter to allow local show_time updates.
14
- class Formatter
15
- # Allow enabling and disabling of time with a singleton.
16
- def self.show_time=(*args)
17
- Mixlib::Log::Formatter.show_time = *args
18
- end
12
+ # Monkeypatch Formatter to allow local show_time updates.
13
+ class Formatter
14
+ # Allow enabling and disabling of time with a singleton.
15
+ def self.show_time=(*args)
16
+ Mixlib::Log::Formatter.show_time = *args
19
17
  end
20
-
21
18
  end
22
19
  end
data/lib/souffle/node.rb CHANGED
@@ -1,70 +1,108 @@
1
- module Souffle
2
- class Node; end
3
- end
1
+ # A node object that's part of a given system.
2
+ class Souffle::Node; end
4
3
 
5
4
  require 'souffle/node/runlist_item'
6
5
  require 'souffle/node/runlist'
7
6
 
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
7
+ # A node object that's part of a given system.
8
+ class Souffle::Node
9
+ attr_accessor :system, :dependencies, :run_list,
10
+ :parents, :children, :name, :options, :provisioner
13
11
 
14
- state_machine :state, :initial => :uninitialized do
15
- end
12
+ # Creates a new souffle node with bare dependencies and run_list.
13
+ #
14
+ # @param [ Fixnum ] parent_multiplier The multiplier for parent nodes.
15
+ def initialize(parent_multiplier=5)
16
+ @dependencies = Souffle::Node::RunList.new
17
+ @run_list = Souffle::Node::RunList.new
18
+ @parents = []
19
+ @children = []
20
+ @options = {
21
+ :attributes => Hash.new
22
+ }
23
+ @parent_multiplier = parent_multiplier
24
+ end
16
25
 
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.
26
+ # Check whether or not a given node depends on another node.
27
+ #
28
+ # @example
29
+ #
30
+ # n1 = Souffle::Node.new
31
+ # n2 = Souffle::Node.new
32
+ #
33
+ # n1.run_list << "role[dns_server]"
34
+ # n2.dependencies << "role[dns_server]"
35
+ # n2.depends_on?(n1)
36
+ #
37
+ # > [ true, [role[dns_server]] ]
38
+ #
39
+ # @param [ Souffle::Node ] node Check to see whether this node depends
40
+ #
41
+ # @return [ Array ] The tuple of [depends_on, dependency_list].
42
+ def depends_on?(node)
43
+ dependency_list = []
44
+ @dependencies.each do |d|
45
+ dependency_list << d if node.run_list.include? d
24
46
  end
47
+ [dependency_list.any?, dependency_list]
48
+ end
25
49
 
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
50
+ # Adds a child node to the current node.
51
+ #
52
+ # @param [ Souffle::Node ] node The node to add as a child.
53
+ #
54
+ # @raise [ InvaidChild ] Children must have dependencies and a run_list.
55
+ def add_child(node)
56
+ unless node.respond_to?(:dependencies) && node.respond_to?(:run_list)
57
+ raise Souffle::Exceptions::InvalidChild,
58
+ "Child must act as a Souffle::Node"
39
59
  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
60
+ unless @children.include? node
61
+ node.parents << self
52
62
  @children.push(node)
53
63
  end
64
+ end
54
65
 
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
66
+ # Iterator method for children.
67
+ #
68
+ # @yield [ Souffle::Node,NilClass ] The child node.
69
+ def each_child
70
+ @children.each { |child| yield child }
71
+ end
72
+
73
+ # Equality comparator for nodes.
74
+ #
75
+ # @param [ Souffle::Node ] other The node to compare against.
76
+ def eql?(other)
77
+ @dependencies == other.dependencies && @run_list == other.run_list
78
+ end
61
79
 
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
80
+ # The dependency weight of a given node.
81
+ #
82
+ # @return [ Fixnum ] The relative weight of a node used for balancing.
83
+ def weight
84
+ @parents.inject(1) { |res, p| res + p.weight * @parent_multiplier }
85
+ end
86
+
87
+ # Tries to fetch an option parameter otherwise it grabs it from config.
88
+ #
89
+ # @param [ Symbol ] opt The option to try and fetch.
90
+ #
91
+ # @return [ String ] The option return value.
92
+ def try_opt(opt)
93
+ if system
94
+ options.fetch(opt, system.try_opt(opt))
95
+ else
96
+ options.fetch(opt, Souffle::Config[opt])
67
97
  end
98
+ rescue
99
+ nil
100
+ end
68
101
 
102
+ # The logging prefix for the given node.
103
+ #
104
+ # @return [ String ] The logging prefix for the given node.
105
+ def log_prefix
106
+ "[#{try_opt(:tag)}: #{name}]"
69
107
  end
70
108
  end
@@ -1,24 +1,22 @@
1
1
  require 'souffle/node/runlist_item'
2
2
 
3
- module Souffle
4
- # A specialized Array that handles runlist items appropriately.
5
- class Node::RunList < Array
3
+ # A specialized Array that handles runlist items appropriately.
4
+ class Souffle::Node::RunList < Array
6
5
 
7
- # Pushes another runlist item onto the runlist array.
8
- #
9
- # @param [ String ] item The runlist item as a string.
10
- def <<(item)
11
- item = Souffle::Node::RunListItem.new(item)
12
- super(item)
13
- end
14
-
15
- # Pushes another item onto the runlist array.
16
- #
17
- # @param [ String ] item The runlist item as a string.
18
- def push(item)
19
- item = Souffle::Node::RunListItem.new(item)
20
- super(item)
21
- end
6
+ # Pushes another runlist item onto the runlist array.
7
+ #
8
+ # @param [ String ] item The runlist item as a string.
9
+ def <<(item)
10
+ item = Souffle::Node::RunListItem.new(item)
11
+ super(item)
12
+ end
22
13
 
14
+ # Pushes another item onto the runlist array.
15
+ #
16
+ # @param [ String ] item The runlist item as a string.
17
+ def push(item)
18
+ item = Souffle::Node::RunListItem.new(item)
19
+ super(item)
23
20
  end
21
+
24
22
  end
@@ -1,46 +1,53 @@
1
1
  require 'souffle/node/runlist_parser'
2
2
 
3
- module Souffle
4
- # A single runlist item, most be parsed and either a recipe or role.
5
- class Node::RunListItem
3
+ # A single runlist item, most be parsed and either a recipe or role.
4
+ class Souffle::Node::RunListItem
6
5
 
7
- # Creates a new runlist item from a string.
8
- #
9
- # @param [ String ] item The runlist string to turn into an object.
10
- # @raise [ InvalidRunlistName, InvalidRunlistType ] Raises exceptions when
11
- # the runlist item or type isn't a proper chef role or recipe.
12
- def initialize(item=nil)
13
- @original_item = item
14
- @item = Souffle::Node::RunListParser.parse(item)
15
- end
6
+ # Creates a new runlist item from a string.
7
+ #
8
+ # @param [ String ] item The runlist string to turn into an object.
9
+ # @raise [ InvalidRunlistName, InvalidRunlistType ] Raises exceptions when
10
+ # the runlist item or type isn't a proper chef role or recipe.
11
+ def initialize(item=nil)
12
+ @original_item = item
13
+ @item = Souffle::Node::RunListParser.parse(item)
14
+ end
16
15
 
17
- # Returns the name of the runlist item.
18
- #
19
- # @return [ String ] The name of the runlist item.
20
- def name
21
- @item["name"]
22
- end
16
+ # Returns the name of the runlist item.
17
+ #
18
+ # @return [ String ] The name of the runlist item.
19
+ def name
20
+ @item["name"]
21
+ end
23
22
 
24
- # Returns the type of the runlist item.
25
- #
26
- # @return [ String ] The type of the runlist item.
27
- def type
28
- @item["type"]
29
- end
23
+ # Returns the type of the runlist item.
24
+ #
25
+ # @return [ String ] The type of the runlist item.
26
+ def type
27
+ @item["type"]
28
+ end
30
29
 
31
- # Returns the RunListItem as it's original string.
32
- def to_s
33
- @original_item
34
- end
30
+ # Returns the RunListItem as it's original string.
31
+ def to_s
32
+ @original_item
33
+ end
35
34
 
36
- # Overriding the default equality comparator to use string representation.
37
- #
38
- # @param [ Souffle::Node::RunListItem ] runlist_item
39
- #
40
- # @return [ true,false ] Whether or not the objects are equal.
41
- def ==(runlist_item)
42
- self.to_s == runlist_item.to_s
43
- end
35
+ # Overriding the default equality comparator to use string representation.
36
+ #
37
+ # @param [ Souffle::Node::RunListItem ] runlist_item
38
+ #
39
+ # @return [ Boolean ] Whether or not the objects are equal.
40
+ def ==(runlist_item)
41
+ self.to_s == runlist_item.to_s
42
+ end
44
43
 
44
+ # Overriding the default equality comparator to use string representation.
45
+ #
46
+ # @param [ Souffle::Node::RunListItem ] runlist_item
47
+ #
48
+ # @return [ Boolean ] Whether or not the objects are equal.
49
+ def eql?(runlist_item)
50
+ self.to_s == runlist_item.to_s
45
51
  end
52
+
46
53
  end
@@ -1,73 +1,71 @@
1
- module Souffle
2
- # The runlist parser singleton.
3
- class Node::RunListParser
1
+ # The runlist parser singleton.
2
+ class Souffle::Node::RunListParser
4
3
 
5
- # The runlist match parser
6
- PARSER = %r{
7
- (?<type> (recipe|role)) {0} # The runlist item type.
8
- (?<name> (.*)) {0} # The runlist item name.
4
+ # The runlist match parser
5
+ PARSER = %r{
6
+ (?<type> (recipe|role)) {0} # The runlist item type.
7
+ (?<name> (.*)) {0} # The runlist item name.
9
8
 
10
- \g<type>\[\g<name>\]
11
- }x
9
+ \g<type>\[\g<name>\]
10
+ }x
12
11
 
13
- class << self
14
- # Checks to see whether the runlist item is a valid recipe or role.
15
- #
16
- # @param [ String ] item The runlist item.
17
- #
18
- # @return [ Hash ] The runlist item as a hash.
19
- def parse(item)
20
- runlist_hash = hashify_match(PARSER.match(item))
21
- gaurentee_valid_keys(runlist_hash)
22
- gaurentee_name_is_word(runlist_hash)
23
- runlist_hash
24
- end
12
+ class << self
13
+ # Checks to see whether the runlist item is a valid recipe or role.
14
+ #
15
+ # @param [ String ] item The runlist item.
16
+ #
17
+ # @return [ Hash ] The runlist item as a hash.
18
+ def parse(item)
19
+ runlist_hash = hashify_match(PARSER.match(item))
20
+ gaurentee_valid_keys(runlist_hash)
21
+ gaurentee_name_is_word(runlist_hash)
22
+ runlist_hash
23
+ end
25
24
 
26
- # Takes the matches and converts them into a hashed version.
27
- #
28
- # @param [ MatchData,nil ] match The MatchData to hashify.
29
- #
30
- # @return [ Hash,nil ] The hashified version of the runlist item.
31
- def hashify_match(match)
32
- return nil if match.nil?
33
- Hash[*match.names.zip(match.captures).flatten]
34
- end
25
+ # Takes the matches and converts them into a hashed version.
26
+ #
27
+ # @param [ MatchData,NilClass ] match The MatchData to hashify.
28
+ #
29
+ # @return [ Hash,NilClass ] The hashified version of the runlist item.
30
+ def hashify_match(match)
31
+ return nil if match.nil?
32
+ Hash[*match.names.zip(match.captures).flatten]
33
+ end
35
34
 
36
- # Tests whether the runlist_hash name and type are valid.
37
- #
38
- # @param [ Hash ] runlist_hash The runlist hash to test.
39
- #
40
- # @raise [ InvalidRunlistName, InvalidRunlistType ] Raises exceptions
41
- # when the runlist match failed, the type wasn't a recipe or role,
42
- # or when the name itself isn't a valid word.
43
- def gaurentee_valid_keys(runlist_hash)
44
- if runlist_hash.nil?
45
- raise Souffle::Exceptions::InvalidRunlistType,
46
- "Type must be one of (role|recipe)"
47
- end
48
- if runlist_hash["name"].nil? or runlist_hash["name"].empty?
49
- raise Souffle::Exceptions::InvalidRunlistName,
50
- "Name cannot be nil or empty."
51
- end
52
- if runlist_hash["type"].nil? or runlist_hash["type"].empty?
53
- raise Souffle::Exceptions::InvalidRunlistType,
54
- "Type cannot be nil or empty and must be one of (role|recipe)"
55
- end
35
+ # Tests whether the runlist_hash name and type are valid.
36
+ #
37
+ # @param [ Hash ] runlist_hash The runlist hash to test.
38
+ #
39
+ # @raise [ InvalidRunlistName, InvalidRunlistType ] Raises exceptions
40
+ # when the runlist match failed, the type wasn't a recipe or role,
41
+ # or when the name itself isn't a valid word.
42
+ def gaurentee_valid_keys(runlist_hash)
43
+ if runlist_hash.nil?
44
+ raise Souffle::Exceptions::InvalidRunlistType,
45
+ "Type must be one of (role|recipe)"
56
46
  end
57
-
58
- # Checks whether the runlist_hash is a valid word.
59
- #
60
- # @param [ Hash ] runlist_hash The runlist hash to test.
61
- #
62
- # @raise [ InvalidRunlistName ] Runlist Name is invalid.
63
- def gaurentee_name_is_word(runlist_hash)
64
- m = /[A-Za-z0-9_:]+/
65
- unless m.match(runlist_hash["name"])[0] == runlist_hash["name"]
66
- raise Souffle::Exceptions::InvalidRunlistName,
67
- "Name must be [A-Za-z0-9_:]."
68
- end
47
+ if runlist_hash["name"].nil? or runlist_hash["name"].empty?
48
+ raise Souffle::Exceptions::InvalidRunlistName,
49
+ "Name cannot be nil or empty."
50
+ end
51
+ if runlist_hash["type"].nil? or runlist_hash["type"].empty?
52
+ raise Souffle::Exceptions::InvalidRunlistType,
53
+ "Type cannot be nil or empty and must be one of (role|recipe)"
69
54
  end
70
55
  end
71
56
 
57
+ # Checks whether the runlist_hash is a valid word.
58
+ #
59
+ # @param [ Hash ] runlist_hash The runlist hash to test.
60
+ #
61
+ # @raise [ InvalidRunlistName ] Runlist Name is invalid.
62
+ def gaurentee_name_is_word(runlist_hash)
63
+ m = /[A-Za-z0-9_\-:]+/
64
+ unless m.match(runlist_hash["name"])[0] == runlist_hash["name"]
65
+ raise Souffle::Exceptions::InvalidRunlistName,
66
+ "Name must be [A-Za-z0-9_-:]."
67
+ end
68
+ end
72
69
  end
70
+
73
71
  end