souffle 0.0.1 → 0.0.2

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.
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,15 +1,52 @@
1
- require 'souffle/provider'
1
+ require 'souffle/template'
2
+ require 'ostruct'
2
3
 
3
4
  # The Vagrant souffle provider.
4
- class Souffle::Provider::Vagrant < Souffle::Provider
5
+ class Souffle::Provider::Vagrant < Souffle::Provider::Base
6
+ attr_reader :vagrant_dir
5
7
 
6
8
  # Setup the internal Vagrant configuration and object.
7
- def setup
9
+ def initialize
10
+ super()
11
+ @vagrant_dir = @system.try_opt(:vagrant_dir)
12
+ create_new_vm_group unless current_folder_has_souffle_config?
13
+ generate_vagrant_config
8
14
  end
9
15
 
10
- # The name of the given provider.
11
- def name; "Vagrant"; end
16
+ # Creates a system using vagrant as the provider.
17
+ #
18
+ # @param [ Souffle::System ] system The system to instantiate.
19
+ # @param [ String ] tag_prefix The tag prefix to use for the system.
20
+ def create_system(system, tag_prefix=nil)
21
+ end
22
+
23
+ # Takes a node definition and begins the provisioning process.
24
+ #
25
+ # @param [ Souffle::Node ] node The node to instantiate.
26
+ # @param [ String ] tag The tag to use for the node.
27
+ def create_node(node, tag=nil)
28
+ end
12
29
 
13
30
  # Noop.
14
31
  def create_raid; end
32
+
33
+ # Checks if the current folder has the souffle configuration object.
34
+ #
35
+ # @return [ Boolean ] Whether or not we're in a souffle Vagrant project.
36
+ def current_folder_has_souffle_config?
37
+ File.exists? "#{Dir.pwd}/souffle.json"
38
+ end
39
+
40
+ # Creates a new virtualmachine group.
41
+ def create_new_vm_group
42
+ end
43
+
44
+ # Generates the multi-vm configuration.
45
+ def generate_vagrant_config
46
+ template = Souffle::Template.new('Vagrantfile.erb')
47
+ temp_binding = OpenStruct.new
48
+ temp_binding.version = Souffle::VERSION
49
+
50
+ template.render(temp_binding)
51
+ end
15
52
  end
@@ -0,0 +1,55 @@
1
+ # Starts up the base provisioner class with system and node state machines.
2
+ class Souffle::Provisioner; end
3
+
4
+ require 'souffle/provisioner/node'
5
+ require 'souffle/provisioner/system'
6
+
7
+ # Starts up the base provisioner class with system and node state machines.
8
+ class Souffle::Provisioner
9
+ attr_reader :provider, :system
10
+
11
+ # Creates a new provisioner.
12
+ def initialize
13
+ @provider = initialize_provider
14
+ @provisioner = nil
15
+ end
16
+
17
+ # Creates the system object from a hash.
18
+ #
19
+ # @param [ Hash ] system_hash The system represented in hash format.
20
+ def setup_system(system_hash)
21
+ @system = Souffle::System.from_hash(system_hash)
22
+ @provider = initialize_provider(
23
+ cleanup_provider(@system.try_opt[:provider]))
24
+ end
25
+
26
+ # Cleans up the provider name to match the providers we have.
27
+ #
28
+ # @param [ String ] provider The name of the provider to use.
29
+ #
30
+ # @return [ String ] The cleaned up provider name.
31
+ def cleanup_provider(provider)
32
+ lookups = {}
33
+ Souffle::Provider.constants.each { |k| lookups[k.to_s.downcase] = k.to_s }
34
+ lookups.fetch([provider.downcase], provider)
35
+ end
36
+
37
+ # Sets up the given provider to be used for the creation of a system.
38
+ #
39
+ # @param [ String ] provider The system provider to use for provisioning.
40
+ def initialize_provider(provider=nil)
41
+ prv = cleanup_provider(Souffle::Config[:provider])
42
+ Souffle::Provider.const_get(prv).new
43
+ rescue Souffle::Exceptions::InvalidAwsKeys => e
44
+ Souffle::Log.error "#{e.message}:\n#{e.backtrace.join("\n")}"
45
+ rescue Exception
46
+ raise Souffle::Exceptions::InvalidProvider,
47
+ "The provider Souffle::Provider::#{prv} does not exist."
48
+ end
49
+
50
+ # Starts the provisioning process keeping a local lookup to the provisioner.
51
+ def begin_provisioning
52
+ @provisioner = Souffle::Provisioner::System.new(@system, @provider)
53
+ end
54
+
55
+ end
@@ -0,0 +1,157 @@
1
+ require 'state_machine'
2
+
3
+ # The node provisioning statemachine.
4
+ class Souffle::Provisioner::Node
5
+ attr_accessor :time_used
6
+
7
+ state_machine :state, :initial => :initializing do
8
+ after_transition any => :handling_error, :do => :error_handler
9
+ after_transition any => :creating, :do => :create
10
+ after_transition :creating => :booting, :do => :boot
11
+ after_transition :booting => :partitioning_device,
12
+ :do => :partition_device
13
+ after_transition :partitioning_device => :installing_mdadm,
14
+ :do => :setup_mdadm
15
+ after_transition :installing_mdadm => :initializing_raid,
16
+ :do => :setup_raid
17
+ after_transition :initializing_raid => :formatting_device,
18
+ :do => :format_device
19
+ after_transition :formatting_device => :ready_to_provision,
20
+ :do => :ready
21
+ after_transition any => :provisioning, :do => :provision
22
+
23
+ event :reclaimed do
24
+ transition any => :creating
25
+ end
26
+
27
+ event :initialized do
28
+ transition :initializing => :creating
29
+ end
30
+
31
+ event :created do
32
+ transition :creating => :booting
33
+ end
34
+
35
+ event :booted do
36
+ transition :booting => :partitioning_device
37
+ end
38
+
39
+ event :partitioned_device do
40
+ transition :partitioning_device => :installing_mdadm
41
+ end
42
+
43
+ event :mdadm_installed do
44
+ transition :installing_mdadm => :initializing_raid
45
+ end
46
+
47
+ event :raid_initialized do
48
+ transition :initializing_raid => :formatting_device
49
+ end
50
+
51
+ event :device_formatted do
52
+ transition :formatting_device => :ready_to_provision
53
+ end
54
+
55
+ event :begin_provision do
56
+ transition :ready_to_provision => :provisioning
57
+ end
58
+
59
+ event :provisioned do
60
+ transition :provisioning => :complete
61
+ end
62
+
63
+ event :error_occurred do
64
+ transition any => :handling_error
65
+ end
66
+
67
+ event :failed do
68
+ transition any => :handling_failure
69
+ end
70
+
71
+ around_transition do |system, transition, block|
72
+ start = Time.now
73
+ block.call
74
+ system.time_used += Time.now - start
75
+ end
76
+ end
77
+
78
+ # Creates a new node provisioner state machine.
79
+ #
80
+ # @param [ Souffle::Node ] node The node to manage.
81
+ # @param [ Fixnum ] max_failures The maximum number of failures.
82
+ def initialize(node, max_failures=3)
83
+ @time_used = 0
84
+ @node = node
85
+ @max_failures = max_failures
86
+ super() # NOTE: This is here to initialize state_machine.
87
+ end
88
+
89
+ # Creates the node from an api or command.
90
+ def create
91
+ Souffle::Log.info "#{@node.log_prefix} Creating a new node..."
92
+ provider.create_node(@node)
93
+ end
94
+
95
+ # Boots up the node and waits for ssh.
96
+ def boot
97
+ Souffle::Log.info "#{@node.log_prefix} Booting node..."
98
+ provider.boot(@node)
99
+ end
100
+
101
+ # Installs and sets up mdadm.
102
+ def setup_mdadm
103
+ Souffle::Log.info "#{@node.log_prefix} Setting up mdadm..."
104
+ provider.setup_mdadm(@node)
105
+ end
106
+
107
+ # Partitions the soon to be raid device.
108
+ def partition_device
109
+ Souffle::Log.info "#{@node.log_prefix} Partitioning the device..."
110
+ provider.partition(@node)
111
+ end
112
+
113
+ # Formats a device to the configured filesystem.
114
+ def format_device
115
+ Souffle::Log.info "#{@node.log_prefix} Formatting the device..."
116
+ provider.format_device(@node)
117
+ end
118
+
119
+ # Sets up raid to the configured raid-level.
120
+ def setup_raid
121
+ Souffle::Log.info "#{@node.log_prefix} Setting up raid..."
122
+ provider.setup_raid(@node)
123
+ end
124
+
125
+ # Notify the logger when the node is ready for provisioning.
126
+ def ready
127
+ Souffle::Log.info "#{@node.log_prefix} Is ready for provisioning..."
128
+ end
129
+
130
+ # Provisions the given node with a chef/chef-solo run.
131
+ def provision
132
+ Souffle::Log.info "#{@node.log_prefix} Provisioning node..."
133
+ provider.provision(@node)
134
+ end
135
+
136
+ # Kills the node entirely.
137
+ def kill
138
+ Souffle::Log.info "#{@node.log_prefix} Killing node..."
139
+ provider.kill(@node)
140
+ end
141
+
142
+ # Kills the node and restarts the creation loop.
143
+ def kill_and_recreate
144
+ Souffle::Log.info "#{@node.log_prefix} Recreating node..."
145
+ provider.kill_and_recreate(@node)
146
+ end
147
+
148
+ # Handles any
149
+ def error_handler
150
+ Souffle::Log.info "#{@node.log_prefix} Handling node error..."
151
+ end
152
+
153
+ # Helper function for the node's system provider.
154
+ def provider
155
+ @node.system.provisioner.provider
156
+ end
157
+ end
@@ -0,0 +1,195 @@
1
+ require 'state_machine'
2
+
3
+ require 'souffle/polling_event'
4
+
5
+ # The system provisioning statemachine.
6
+ class Souffle::Provisioner::System
7
+ attr_accessor :time_used, :provider
8
+
9
+ attr_reader :max_failures
10
+
11
+ state_machine :state, :initial => :initializing do
12
+ after_transition any => :handling_error, :do => :error_handler
13
+ after_transition :initializing => :creating, :do => :create
14
+ after_transition :creating => :provisioning, :do => :provision
15
+ after_transition any => :initializing, :do => :create
16
+
17
+ event :initialized do
18
+ transition :initializing => :creating
19
+ end
20
+
21
+ event :created do
22
+ transition :creating => :provisioning
23
+ end
24
+
25
+ event :provisioned do
26
+ transition :provisioning => :complete
27
+ end
28
+
29
+ event :error_occurred do
30
+ transition any => :handling_error
31
+ end
32
+
33
+ event :creation_halted do
34
+ transition any => :failed
35
+ end
36
+
37
+ event :reclaimed do
38
+ transition any => :initializing
39
+ end
40
+
41
+ around_transition do |system, transition, block|
42
+ start = Time.now
43
+ block.call
44
+ system.time_used += Time.now - start
45
+ end
46
+ end
47
+
48
+ # Creates a new system using a specific provider.
49
+ #
50
+ # @param [ Souffle::System ] system The system to provision.
51
+ # @param [ Souffle::Provider::Base ] provider The provider to use.
52
+ # @param [ Fixnum ] max_failures the maximum number of failures.
53
+ # @param [ Fixnum ] timeout The maximum time to wait for node creation.
54
+ def initialize(system, provider, max_failures=3, timeout=500)
55
+ @failures = 0
56
+ @system = system
57
+ @provider = provider
58
+ @time_used = 0
59
+ @timeout = timeout
60
+ @max_failures = max_failures
61
+ super() # NOTE: This is here to initialize state_machine.
62
+ end
63
+
64
+ # Creates the system from an api or command.
65
+ def create
66
+ Souffle::Log.info "[#{system_tag}] Creating a new system..."
67
+ @system.nodes.each do |node|
68
+ node.provisioner = Souffle::Provisioner::Node.new(node)
69
+ node.provisioner.initialized
70
+ end
71
+ wait_until_created
72
+ end
73
+
74
+ # Provisioning the system.
75
+ #
76
+ # @todo We should really have these provisioned with fibers.
77
+ def provision
78
+ Souffle::Log.info "[#{system_tag}] Provisioning the system..."
79
+ @system.rebalance_nodes
80
+ @system.nodes.each do |node|
81
+ when_parents_are_complete(node) { node.provisioner.begin_provision }
82
+ end
83
+ wait_until_complete
84
+ end
85
+
86
+ # Wait until all of the parent nodes are in a completed state and yield.
87
+ def when_parents_are_complete(node)
88
+ total_nodes = node.parents.size
89
+ if total_nodes == 0
90
+ yield if block_given?
91
+ all_complete = true
92
+ else
93
+ all_complete = false
94
+ end
95
+ timer = EM::PeriodicTimer.new(2) do
96
+ nodes_complete = node.parents.select do |n|
97
+ n.provisioner.state == "complete"
98
+ end.size
99
+
100
+ if nodes_complete == total_nodes
101
+ all_complete = true
102
+ timer.cancel
103
+ yield if block_given?
104
+ end
105
+ end
106
+
107
+ EM::Timer.new(@timeout) do
108
+ unless all_complete
109
+ Souffle::Log.error "[#{system_tag}] Parent creation timeout reached."
110
+ timer.cancel
111
+ error_occurred
112
+ end
113
+ end
114
+ end
115
+
116
+ # Kills the system.
117
+ def kill_system
118
+ # @provider.kill(@system.nodes)
119
+ end
120
+
121
+ # Handles the error state and recreates the system
122
+ def error_handler
123
+ @failures += 1
124
+ if @failures >= @max_failures
125
+ Souffle::Log.error "[#{system_tag}] Complete failure. Halting Creation."
126
+ creation_halted
127
+ else
128
+ err_msg = "[#{system_tag}] Error creating system. "
129
+ err_msg << "Killing and recreating..."
130
+ Souffle::Log.error(err_msg)
131
+ kill_system
132
+ reclaimed
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ # Helper function for the system tag.
139
+ #
140
+ # @param [ String ] The system or global tag.
141
+ def system_tag
142
+ @system.try_opt(:tag)
143
+ end
144
+
145
+ # Wait until all of the nodes are ready to be provisioned and then continue.
146
+ def wait_until_created
147
+ total_nodes = @system.nodes.size
148
+ all_created = false
149
+ timer = EM::PeriodicTimer.new(2) do
150
+ nodes_ready = @system.nodes.select do |n|
151
+ n.provisioner.state == "ready_to_provision"
152
+ end.size
153
+
154
+ if nodes_ready == total_nodes
155
+ all_created = true
156
+ timer.cancel
157
+ created
158
+ end
159
+ end
160
+
161
+ EM::Timer.new(@timeout) do
162
+ unless all_created
163
+ Souffle::Log.error "[#{system_tag}] System creation timeout reached."
164
+ timer.cancel
165
+ error_occurred
166
+ end
167
+ end
168
+ end
169
+
170
+ # Wait until all of the nodes are provisioned and then continue.
171
+ def wait_until_complete
172
+ total_nodes = @system.nodes.size
173
+ all_complete = false
174
+ timer = EM::PeriodicTimer.new(2) do
175
+ nodes_ready = @system.nodes.select do |n|
176
+ n.provisioner.state == "complete"
177
+ end.size
178
+
179
+ if nodes_ready == total_nodes
180
+ all_complete = true
181
+ timer.cancel
182
+ created
183
+ end
184
+ end
185
+
186
+ EM::Timer.new(@timeout) do
187
+ unless all_complete
188
+ Souffle::Log.error "[#{system_tag}] System provision timeout reached."
189
+ timer.cancel
190
+ error_occurred
191
+ end
192
+ end
193
+ end
194
+
195
+ end