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,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