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