zergrush 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,63 @@
1
+ #--
2
+
3
+ # Copyright 2014 by MTN Sattelite Communications
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to
7
+ # deal in the Software without restriction, including without limitation the
8
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9
+ # sell copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ # IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'thor/group'
25
+ module Zerg
26
+ module Generators
27
+ class HiveGen < Thor::Group
28
+ include Thor::Actions
29
+
30
+ class_option :force, :type => :boolean
31
+
32
+ def self.source_root
33
+ File.join(File.dirname(__FILE__), "task")
34
+ end
35
+
36
+ def create_hive
37
+ load_path = (ENV['HIVE_CWD'] == nil) ? File.join("#{Dir.pwd}", ".hive") : File.join("#{ENV['HIVE_CWD']}", ".hive")
38
+ empty_directory "#{File.join(load_path, "driver")}"
39
+ end
40
+
41
+ def copy_sample_task
42
+ load_path = (ENV['HIVE_CWD'] == nil) ? File.join("#{Dir.pwd}", ".hive") : File.join("#{ENV['HIVE_CWD']}", ".hive")
43
+ opts = {
44
+ :instances => 3,
45
+ :drivertype => "vagrant",
46
+ :providertype => "virtualbox",
47
+ :baseboxpath => "http://files.vagrantup.com/precise32.box",
48
+ :privatenetwork => true
49
+ }
50
+ template("template.ke", "#{File.join(load_path, "helloworld.ke")}", opts)
51
+
52
+ opts = {
53
+ :instances => 3,
54
+ :drivertype => "vagrant",
55
+ :providertype => "aws",
56
+ :baseboxpath => "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box",
57
+ :privatenetwork => false
58
+ }
59
+ template("awstemplate.ke", "#{File.join(load_path, "helloaws.ke")}", opts)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,28 @@
1
+ {
2
+ "instances": <%= config[:instances] %>,
3
+ "tasks": [
4
+ {
5
+ "type": "shell",
6
+ "inline": "echo \"ZERG RUSH PRIME!\""
7
+ }
8
+ ],
9
+ "vm": {
10
+ "driver": {
11
+ "drivertype": "<%= config[:drivertype] %>",
12
+ "providertype": "<%= config[:providertype] %>",
13
+ "provider_options": [
14
+ "<%= config[:providertype] %>.instance_type = 't1.micro'",
15
+ "<%= config[:providertype] %>.access_key_id = \"#{ENV['AWS_ACCESS_KEY_ID']}\"",
16
+ "<%= config[:providertype] %>.secret_access_key = \"#{ENV['AWS_SECRET_ACCESS_KEY']}\"",
17
+ "<%= config[:providertype] %>.keypair_name = \"#{ENV['AWS_KEY_PAIR']}\"",
18
+ "<%= config[:providertype] %>.ami = 'ami-3fec7956'",
19
+ "<%= config[:providertype] %>.region = 'us-east-1'",
20
+ "<%= config[:providertype] %>.security_groups = [ \"#{ENV['AWS_SECURITY_GROUP']}\" ]",
21
+ "override.ssh.username = 'ubuntu'",
22
+ "override.ssh.private_key_path = \"#{ENV['AWS_PRIVATE_KEY_PATH']}\""
23
+ ]
24
+ },
25
+ "basebox": "<%= config[:baseboxpath] %>",
26
+ "private_network": <%= config[:privatenetwork] %>
27
+ }
28
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "instances": <%= config[:instances] %>,
3
+ "tasks": [
4
+ {
5
+ "type": "shell",
6
+ "inline": "echo \"ZERG RUSH!\""
7
+ }
8
+ ],
9
+ "vm": {
10
+ "driver": {
11
+ "drivertype": "<%= config[:drivertype] %>",
12
+ "providertype": "<%= config[:providertype] %>",
13
+ "provider_options": [
14
+ "<%= config[:providertype] %>.gui = false",
15
+ "<%= config[:providertype] %>.memory = 256",
16
+ "# adjust for DNS weirdness in ubuntu 12.04",
17
+ "<%= config[:providertype] %>.customize ['modifyvm', :id, '--natdnsproxy1', 'off']",
18
+ "<%= config[:providertype] %>.customize ['modifyvm', :id, '--natdnshostresolver1', 'off']",
19
+ "# set virtio type on the NIC driver. Better performance for large traffic bursts",
20
+ "<%= config[:providertype] %>.customize ['modifyvm', :id, '--nictype1', 'virtio']",
21
+ "<%= config[:providertype] %>.customize ['modifyvm', :id, '--nictype2', 'virtio']",
22
+ "<%= config[:providertype] %>.customize ['modifyvm', :id, '--nictype3', 'virtio']"
23
+ ]
24
+ },
25
+ "basebox": "<%= config[:baseboxpath] %>",
26
+ "private_network": <%= config[:privatenetwork] %>
27
+ }
28
+ }
data/lib/zerg/hive.rb ADDED
@@ -0,0 +1,145 @@
1
+ #--
2
+
3
+ # Copyright 2014 by MTN Sattelite Communications
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to
7
+ # deal in the Software without restriction, including without limitation the
8
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9
+ # sell copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ # IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'json'
25
+ require 'awesome_print'
26
+ require 'json-schema'
27
+ require 'fileutils'
28
+ require 'singleton'
29
+ require 'highline/import'
30
+
31
+ module Zerg
32
+ class Hive
33
+ include Singleton
34
+ attr_reader :hive
35
+
36
+ def loaded
37
+ @loaded || false
38
+ end
39
+
40
+ def load
41
+ if loaded
42
+ return
43
+ end
44
+
45
+ load_path = (ENV['HIVE_CWD'] == nil) ? File.join("#{Dir.pwd}", ".hive") : File.join("#{ENV['HIVE_CWD']}", ".hive")
46
+ abort("ERROR: '.hive' not found at #{load_path}. Run 'zerg init', change HIVE_CWD or run zerg from a different path.") unless File.directory?(load_path)
47
+
48
+ # load all .ke files into one big hash
49
+ @hive = Hash.new
50
+ Dir.glob(File.join("#{load_path}", "*.ke")) do |ke_file|
51
+ # do work on files ending in .rb in the desired directory
52
+ begin
53
+ ke_file_hash = JSON.parse( IO.read(ke_file) )
54
+ @hive[File.basename(ke_file, ".ke")] = ke_file_hash
55
+ rescue JSON::ParserError
56
+ abort("ERROR: Could not parse #{ke_file}. Likely invalid JSON.")
57
+ end
58
+ end
59
+
60
+ @loaded = true
61
+ end
62
+
63
+ def self.list
64
+ instance.load
65
+
66
+ # iterate over hive configs and print out the names
67
+ puts "Current hive tasks are:"
68
+
69
+ if instance.loaded == false
70
+ puts "No hive loaded!"
71
+ puts "FAILURE!"
72
+ return
73
+ end
74
+
75
+ if instance.hive.empty?()
76
+ puts "No tasks defined in hive."
77
+ return
78
+ end
79
+
80
+ puts "#{instance.hive.length} tasks in current hive:"
81
+ puts "#{instance.hive.keys.ai}"
82
+ end
83
+
84
+ def self.verify
85
+ load_path = (ENV['HIVE_CWD'] == nil) ? File.join("#{Dir.pwd}", ".hive") : File.join("#{ENV['HIVE_CWD']}", ".hive")
86
+ abort("ERROR: '.hive' not found at #{load_path}. Run 'zerg init', change HIVE_CWD or run zerg from a different path.") unless File.directory?(load_path)
87
+
88
+ Dir.glob(File.join("#{load_path}", "*.ke")) do |ke_file|
89
+ begin
90
+ ke_file_hash = JSON.parse( IO.read(ke_file) )
91
+
92
+ # verify against schema.
93
+ errors = JSON::Validator.fully_validate(File.join("#{File.dirname(__FILE__)}", "../../data/ke.schema"), ke_file_hash, :errors_as_objects => true)
94
+ unless errors.empty?
95
+ abort("ERROR: #{ke_file} failed validation. Errors: #{errors.ai}")
96
+ end
97
+ rescue JSON::ParserError => err
98
+ abort("ERROR: Could not parse #{ke_file}. Likely invalid JSON.")
99
+ end
100
+ end
101
+
102
+ puts "SUCCESS!"
103
+ end
104
+
105
+ def self.import(file, force)
106
+ load_path = (ENV['HIVE_CWD'] == nil) ? File.join("#{Dir.pwd}", ".hive") : File.join("#{ENV['HIVE_CWD']}", ".hive")
107
+ abort("ERROR: '.hive' not found at #{load_path}. Run 'zerg init', change HIVE_CWD or run zerg from a different path.") unless File.directory?(load_path)
108
+ abort("ERROR: '#{file}' not found!") unless File.exist?(file)
109
+ abort("ERROR: '#{File.basename(file)}' already exists in hive!") unless !File.exist?(File.join(load_path, File.basename(file))) || force == true
110
+
111
+ # check the file against schema.
112
+ begin
113
+ ke_file_hash = JSON.parse( IO.read(file) )
114
+ errors = JSON::Validator.fully_validate(File.join("#{File.dirname(__FILE__)}", "../../data/ke.schema"), ke_file_hash, :errors_as_objects => true)
115
+ abort("ERROR: #{file} failed validation. Errors: #{errors.ai}") unless errors.empty?
116
+
117
+ FileUtils.cp(file, File.join(load_path, File.basename(file)))
118
+ rescue JSON::ParserError => err
119
+ abort("ERROR: Could not parse #{file}. Likely invalid JSON.")
120
+ end
121
+ puts "SUCCESS!"
122
+ end
123
+
124
+ def self.remove(taskname, force)
125
+ load_path = (ENV['HIVE_CWD'] == nil) ? File.join("#{Dir.pwd}", ".hive") : File.join("#{ENV['HIVE_CWD']}", ".hive")
126
+ abort("ERROR: '.hive' not found at #{load_path}. Run 'zerg init', change HIVE_CWD or run zerg from a different path.") unless File.directory?(load_path)
127
+ abort("ERROR: '#{taskname}' not found!") unless File.exist?(File.join(load_path, "#{taskname}.ke"))
128
+
129
+ # check the file against schema.
130
+ taskfile = File.join(load_path, "#{taskname}.ke")
131
+
132
+ agreed = true
133
+ if force != true
134
+ agreed = agree("Remove task #{taskname}?")
135
+ end
136
+
137
+ abort("Cancelled!") unless agreed == true
138
+
139
+ FileUtils.rm_rf(File.join(load_path, "driver", taskname))
140
+ FileUtils.rm(File.join(load_path, "#{taskname}.ke"))
141
+
142
+ puts "SUCCESS!"
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,232 @@
1
+ #--
2
+
3
+ # Copyright 2014 by MTN Sattelite Communications
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to
7
+ # deal in the Software without restriction, including without limitation the
8
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9
+ # sell copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ # IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'awesome_print'
25
+ require 'fileutils'
26
+ require 'erb'
27
+ require 'rbconfig'
28
+
29
+ module Zerg
30
+ class Runner
31
+
32
+ def check_provider(driver, provider)
33
+ if driver == "vagrant"
34
+ if provider == "aws"
35
+ aws_pid = Process.spawn("vagrant plugin list | grep vagrant-aws")
36
+ Process.wait(aws_pid)
37
+
38
+ if $?.exitstatus != 0
39
+ aws_pid = Process.spawn("vagrant plugin install vagrant-aws")
40
+ Process.wait(aws_pid)
41
+ abort("ERROR: vagrant-aws installation failed!") unless $?.exitstatus == 0
42
+ end
43
+ elsif provider == "libvirt"
44
+ abort("ERROR: libvirt is only supported on a linux host!") unless /linux|arch/i === RbConfig::CONFIG['host_os']
45
+
46
+ libvirt_pid = Process.spawn("vagrant plugin list | grep vagrant-libvirt")
47
+ Process.wait(libvirt_pid)
48
+
49
+ if $?.exitstatus != 0
50
+ libvirt_pid = Process.spawn("vagrant plugin install vagrant-libvirt")
51
+ Process.wait(libvirt_pid)
52
+ abort("ERROR: vagrant-libvirt installation failed! Refer to https://github.com/pradels/vagrant-libvirt to install missing dependencies, if any.") unless $?.exitstatus == 0
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ # cross platform way of checking if command is available in PATH
59
+ def which(cmd)
60
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
61
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
62
+ exts.each { |ext|
63
+ exe = File.join(path, "#{cmd}#{ext}")
64
+ return exe if File.executable? exe
65
+ }
66
+ end
67
+ return nil
68
+ end
69
+
70
+ def process(taskname, task, debug)
71
+ puts ("Will perform task #{taskname} with contents:\n #{task.ai}")
72
+
73
+ # render driver template
74
+ renderer = DriverRenderer.new(
75
+ task["vm"],
76
+ taskname,
77
+ task["instances"],
78
+ task["synced_folders"],
79
+ task["tasks"])
80
+
81
+ renderer.render
82
+
83
+ # do we need additional plugins?
84
+ task["tasks"].each { |task|
85
+ if task["type"] == "chef_client" || task["type"] == "chef_solo"
86
+ omnibus_pid = Process.spawn("vagrant plugin list | grep vagrant-omnibus")
87
+ Process.wait(omnibus_pid)
88
+
89
+ if $?.exitstatus != 0
90
+ omnibus_pid = Process.spawn("vagrant plugin install vagrant-omnibus")
91
+ Process.wait(aws_pid)
92
+ abort("ERROR: vagrant-omnibus installation failed!") unless $?.exitstatus == 0
93
+ end
94
+ break;
95
+ end
96
+ }
97
+
98
+ run(taskname, task["vm"]["driver"]["drivertype"], task["vm"]["driver"]["providertype"], task["instances"], debug)
99
+ end
100
+
101
+ def cleanup(taskname, task, debug)
102
+ abort("ERROR: Vagrant not installed!") unless which("vagrant") != nil
103
+ puts ("Will cleanup task #{taskname}...")
104
+
105
+ # TODO: generalize for multiple drivers
106
+ # render driver template
107
+ renderer = DriverRenderer.new(
108
+ task["vm"],
109
+ taskname,
110
+ task["instances"],
111
+ task["synced_folders"],
112
+ task["tasks"])
113
+ renderer.render
114
+
115
+ check_provider(task["vm"]["driver"]["drivertype"], task["vm"]["driver"]["providertype"])
116
+
117
+ # run vagrant cleanup
118
+ debug_string = (debug == true) ? " --debug" : ""
119
+
120
+ for index in 0..task["instances"] - 1
121
+ cleanup_pid = Process.spawn(
122
+ {
123
+ "VAGRANT_CWD" => File.join("#{Dir.pwd}", ".hive", "driver", task["vm"]["driver"]["drivertype"], taskname),
124
+ "VAGRANT_DEFAULT_PROVIDER" => task["vm"]["driver"]["providertype"]
125
+ },
126
+ "vagrant destroy zergling_#{index} --force#{debug_string}")
127
+ Process.wait(cleanup_pid)
128
+ abort("ERROR: vagrant failed!") unless $?.exitstatus == 0
129
+ end
130
+
131
+ cleanup_pid = Process.spawn(
132
+ {
133
+ "VAGRANT_CWD" => File.join("#{Dir.pwd}", ".hive", "driver", task["vm"]["driver"]["drivertype"], taskname)
134
+ },
135
+ "vagrant box remove zergling_#{taskname}_#{task["vm"]["driver"]["providertype"]}#{debug_string} #{task["vm"]["driver"]["providertype"]}")
136
+ Process.wait(cleanup_pid)
137
+ end
138
+
139
+ def run(taskname, driver, provider, instances, debug)
140
+ # TODO: generalize to multiple drivers
141
+ abort("ERROR: Vagrant not installed!") unless which("vagrant") != nil
142
+
143
+ check_provider(driver, provider)
144
+
145
+ debug_string = (debug == true) ? " --debug" : ""
146
+
147
+ # bring up all of the VMs first.
148
+ puts("Starting vagrant in #{File.join("#{Dir.pwd}", ".hive", "driver", driver, taskname)}")
149
+ for index in 0..instances - 1
150
+ create_pid = Process.spawn(
151
+ {
152
+ "VAGRANT_CWD" => File.join("#{Dir.pwd}", ".hive", "driver", driver, taskname)
153
+ },
154
+ "vagrant up zergling_#{index} --no-provision --provider=#{provider}#{debug_string}")
155
+ Process.wait(create_pid)
156
+
157
+ if $?.exitstatus != 0
158
+ puts "ERROR: vagrant failed while creating one of the VMs. Will clean task #{taskname}:"
159
+ self.class.clean(taskname, debug)
160
+ abort("ERROR: vagrant failed!")
161
+ end
162
+ end
163
+
164
+ puts("Running tasks in vagrant virtual machines...")
165
+ # and provision them all at once (sort of)
166
+ provisioners = Array.new
167
+ provision_pid = nil
168
+ for index in 0..instances - 1
169
+ provision_pid = Process.spawn(
170
+ {
171
+ "VAGRANT_CWD" => File.join("#{Dir.pwd}", ".hive", "driver", driver, taskname),
172
+ "VAGRANT_DEFAULT_PROVIDER" => "#{provider}"
173
+ },
174
+ "vagrant provision zergling_#{index}#{debug_string}")
175
+ provisioners.push({:name => "zergling_#{index}", :pid => provision_pid})
176
+ end
177
+
178
+ # wait for everything to finish...
179
+ errors = Array.new
180
+ lock = Mutex.new
181
+ provisioners.each { |provisioner|
182
+ Thread.new {
183
+ Process.wait(provisioner[:pid]);
184
+ lock.synchronize do
185
+ errors.push(provisioner[:name]) unless $?.exitstatus == 0
186
+ end
187
+ }.join
188
+ }
189
+
190
+ puts("DONE! Halting all vagrant virtual machines...")
191
+ # halt all machines
192
+ halt_pid = nil
193
+ for index in 0..instances - 1
194
+ halt_pid = Process.spawn(
195
+ {
196
+ "VAGRANT_CWD" => File.join("#{Dir.pwd}", ".hive", "driver", driver, taskname),
197
+ "VAGRANT_DEFAULT_PROVIDER" => "#{provider}"
198
+ },
199
+ "vagrant halt zergling_#{index}#{debug_string}")
200
+ Process.wait(halt_pid)
201
+ abort("ERROR: vagrant halt failed on machine zergling_#{index}!") unless $?.exitstatus == 0
202
+ end
203
+
204
+ abort("ERROR: Finished with errors in: #{errors.to_s}") unless errors.length == 0
205
+ puts("SUCCESS!")
206
+ end
207
+
208
+ def self.rush(task, debug)
209
+ # load the hive first
210
+ Zerg::Hive.instance.load
211
+
212
+ puts "Loaded hive. Looking for task #{task}..."
213
+ abort("ERROR: Task #{task} not found in current hive!") unless Zerg::Hive.instance.hive.has_key?(task)
214
+
215
+ # grab the current task hash and parse it out
216
+ runner = Runner.new
217
+ runner.process(task, Zerg::Hive.instance.hive[task], debug);
218
+ end
219
+
220
+ def self.clean(task, debug)
221
+ # load the hive first
222
+ Zerg::Hive.instance.load
223
+
224
+ puts "Loaded hive. Looking for task #{task}..."
225
+ abort("ERROR: Task #{task} not found in current hive!") unless Zerg::Hive.instance.hive.has_key?(task)
226
+
227
+ runner = Runner.new
228
+ runner.cleanup(task, Zerg::Hive.instance.hive[task], debug);
229
+ puts("SUCCESS!")
230
+ end
231
+ end
232
+ end