zergrush 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,227 @@
1
+ Feature: Hive
2
+ When I init, verify list and import hive configs
3
+ As a CLI
4
+ I want to see success
5
+
6
+ Scenario: Initializing hive
7
+ When I run `zerg init`
8
+ Then the following files should exist:
9
+ | .hive/helloworld.ke | .hive/helloaws.ke |
10
+ Then the file ".hive/helloworld.ke" should contain:
11
+ """
12
+ {
13
+ "instances": 3,
14
+ "tasks": [
15
+ {
16
+ "type": "shell",
17
+ "inline": "echo \"ZERG RUSH!\""
18
+ }
19
+ ],
20
+ "vm": {
21
+ "driver": {
22
+ "drivertype": "vagrant",
23
+ "providertype": "virtualbox",
24
+ "provider_options": [
25
+ "virtualbox.gui = false",
26
+ "virtualbox.memory = 256",
27
+ "# adjust for DNS weirdness in ubuntu 12.04",
28
+ "virtualbox.customize ['modifyvm', :id, '--natdnsproxy1', 'off']",
29
+ "virtualbox.customize ['modifyvm', :id, '--natdnshostresolver1', 'off']",
30
+ "# set virtio type on the NIC driver. Better performance for large traffic bursts",
31
+ "virtualbox.customize ['modifyvm', :id, '--nictype1', 'virtio']",
32
+ "virtualbox.customize ['modifyvm', :id, '--nictype2', 'virtio']",
33
+ "virtualbox.customize ['modifyvm', :id, '--nictype3', 'virtio']"
34
+ ]
35
+ },
36
+ "basebox": "http://files.vagrantup.com/precise32.box",
37
+ "private_network": true
38
+ }
39
+ }
40
+ """
41
+
42
+ Then the file ".hive/helloaws.ke" should contain:
43
+ """
44
+ {
45
+ "instances": 3,
46
+ "tasks": [
47
+ {
48
+ "type": "shell",
49
+ "inline": "echo \"ZERG RUSH PRIME!\""
50
+ }
51
+ ],
52
+ "vm": {
53
+ "driver": {
54
+ "drivertype": "vagrant",
55
+ "providertype": "aws",
56
+ "provider_options": [
57
+ "aws.instance_type = 't1.micro'",
58
+ "aws.access_key_id = \"#{ENV['AWS_ACCESS_KEY_ID']}\"",
59
+ "aws.secret_access_key = \"#{ENV['AWS_SECRET_ACCESS_KEY']}\"",
60
+ "aws.keypair_name = \"#{ENV['AWS_KEY_PAIR']}\"",
61
+ "aws.ami = 'ami-3fec7956'",
62
+ "aws.region = 'us-east-1'",
63
+ "aws.security_groups = [ \"#{ENV['AWS_SECURITY_GROUP']}\" ]",
64
+ "override.ssh.username = 'ubuntu'",
65
+ "override.ssh.private_key_path = \"#{ENV['AWS_PRIVATE_KEY_PATH']}\""
66
+ ]
67
+ },
68
+ "basebox": "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box",
69
+ "private_network": false
70
+ }
71
+ }
72
+ """
73
+ And the exit status should be 0
74
+
75
+ @no-clobber
76
+ Scenario: Verifying hive
77
+ When I run `zerg hive verify`
78
+ Then the output should contain:
79
+ """
80
+ SUCCESS!
81
+ """
82
+ And the exit status should be 0
83
+
84
+ When I run `zerg hive list`
85
+ Then the output should contain:
86
+ """
87
+ 2 tasks in current hive:
88
+ [
89
+ [0] "helloaws",
90
+ [1] "helloworld"
91
+ ]
92
+ """
93
+ And the exit status should be 0
94
+
95
+ @no-clobber
96
+ Scenario: Importing a hive task
97
+ Given a file named "arubatask.ke" with:
98
+ """
99
+ {
100
+ "instances": 1,
101
+ "tasks": [
102
+ {
103
+ "type": "shell",
104
+ "inline": "echo \"ARRRRRRRUUUUUBAAAAAAAA!\""
105
+ }
106
+ ],
107
+ "vm": {
108
+ "driver": {
109
+ "drivertype": "vagrant",
110
+ "providertype": "aws",
111
+ "provider_options": [
112
+ "aws.instance_type = 't1.micro'"
113
+ ]
114
+ },
115
+ "basebox": "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box",
116
+ "private_network": false
117
+ }
118
+ }
119
+ """
120
+
121
+ When I run `zerg hive import arubatask.ke`
122
+ Then the output should contain:
123
+ """
124
+ SUCCESS!
125
+ """
126
+ And the exit status should be 0
127
+
128
+ When I run `zerg hive import arubatask.ke`
129
+ Then the output should contain:
130
+ """
131
+ ERROR: 'arubatask.ke' already exists in hive!
132
+ """
133
+ And the exit status should be 1
134
+
135
+ When I run `zerg hive import arubatask.ke --force`
136
+ Then the output should contain:
137
+ """
138
+ SUCCESS!
139
+ """
140
+ And the exit status should be 0
141
+
142
+ @no-clobber
143
+ Scenario: Listing hive tasks
144
+ When I run `zerg hive list`
145
+ Then the output should contain:
146
+ """
147
+ 3 tasks in current hive:
148
+ [
149
+ [0] "arubatask",
150
+ [1] "helloaws",
151
+ [2] "helloworld"
152
+ ]
153
+ """
154
+ And the exit status should be 0
155
+
156
+ Scenario: Overriding hive location
157
+ Given a directory named "overriden/hive/dir"
158
+ Given I set the environment variables to:
159
+ | variable | value |
160
+ | HIVE_CWD | ./overriden/hive/dir |
161
+ When I run `zerg init`
162
+ Then the following files should exist:
163
+ | overriden/hive/dir/.hive/helloworld.ke | overriden/hive/dir/.hive/helloaws.ke |
164
+ Then the file "overriden/hive/dir/.hive/helloworld.ke" should contain:
165
+ """
166
+ {
167
+ "instances": 3,
168
+ "tasks": [
169
+ {
170
+ "type": "shell",
171
+ "inline": "echo \"ZERG RUSH!\""
172
+ }
173
+ ],
174
+ "vm": {
175
+ "driver": {
176
+ "drivertype": "vagrant",
177
+ "providertype": "virtualbox",
178
+ "provider_options": [
179
+ "virtualbox.gui = false",
180
+ "virtualbox.memory = 256",
181
+ "# adjust for DNS weirdness in ubuntu 12.04",
182
+ "virtualbox.customize ['modifyvm', :id, '--natdnsproxy1', 'off']",
183
+ "virtualbox.customize ['modifyvm', :id, '--natdnshostresolver1', 'off']",
184
+ "# set virtio type on the NIC driver. Better performance for large traffic bursts",
185
+ "virtualbox.customize ['modifyvm', :id, '--nictype1', 'virtio']",
186
+ "virtualbox.customize ['modifyvm', :id, '--nictype2', 'virtio']",
187
+ "virtualbox.customize ['modifyvm', :id, '--nictype3', 'virtio']"
188
+ ]
189
+ },
190
+ "basebox": "http://files.vagrantup.com/precise32.box",
191
+ "private_network": true
192
+ }
193
+ }
194
+ """
195
+
196
+ Then the file "overriden/hive/dir/.hive/helloaws.ke" should contain:
197
+ """
198
+ {
199
+ "instances": 3,
200
+ "tasks": [
201
+ {
202
+ "type": "shell",
203
+ "inline": "echo \"ZERG RUSH PRIME!\""
204
+ }
205
+ ],
206
+ "vm": {
207
+ "driver": {
208
+ "drivertype": "vagrant",
209
+ "providertype": "aws",
210
+ "provider_options": [
211
+ "aws.instance_type = 't1.micro'",
212
+ "aws.access_key_id = \"#{ENV['AWS_ACCESS_KEY_ID']}\"",
213
+ "aws.secret_access_key = \"#{ENV['AWS_SECRET_ACCESS_KEY']}\"",
214
+ "aws.keypair_name = \"#{ENV['AWS_KEY_PAIR']}\"",
215
+ "aws.ami = 'ami-3fec7956'",
216
+ "aws.region = 'us-east-1'",
217
+ "aws.security_groups = [ \"#{ENV['AWS_SECURITY_GROUP']}\" ]",
218
+ "override.ssh.username = 'ubuntu'",
219
+ "override.ssh.private_key_path = \"#{ENV['AWS_PRIVATE_KEY_PATH']}\""
220
+ ]
221
+ },
222
+ "basebox": "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box",
223
+ "private_network": false
224
+ }
225
+ }
226
+ """
227
+ And the exit status should be 0
@@ -0,0 +1,4 @@
1
+ require 'aruba/cucumber'
2
+ Before do
3
+ @aruba_timeout_seconds = 120
4
+ end
@@ -0,0 +1,31 @@
1
+ Feature: Tasks
2
+ When I clean a task
3
+ As a CLI
4
+ I want to see success
5
+
6
+ @no-clobber
7
+ Scenario: Cleaning a task
8
+ When I run `zerg init`
9
+ When I run `zerg clean helloworld`
10
+ Then the output should contain:
11
+ """
12
+ SUCCESS!
13
+ """
14
+ And the exit status should be 0
15
+
16
+ Scenario: Cleaning a task that does not exist
17
+ When I run `zerg init`
18
+ When I run `zerg clean HRRGRHBGRGHLURGH`
19
+ Then the output should contain:
20
+ """
21
+ ERROR: Task HRRGRHBGRGHLURGH not found in current hive!
22
+ """
23
+ And the exit status should be 1
24
+
25
+ Scenario: Cleaning a task without hive
26
+ When I run `zerg clean helloaws`
27
+ Then the output should contain:
28
+ """
29
+ ERROR:
30
+ """
31
+ And the exit status should be 1
data/lib/zerg.rb ADDED
@@ -0,0 +1,30 @@
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 "zerg/hive"
25
+ require "zerg/runner"
26
+ require "zerg/driver_renderer"
27
+
28
+ module Zerg
29
+
30
+ end
data/lib/zerg/cli.rb ADDED
@@ -0,0 +1,85 @@
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'
25
+ require 'zerg'
26
+ require 'zerg/generators/hivegen'
27
+
28
+ module Zerg
29
+ class HiveCLI < Thor
30
+ class_option :force, :type => :boolean, :banner => "force overwrite of files"
31
+
32
+ # hack (https://github.com/erikhuda/thor/issues/261#issuecomment-16880836)
33
+ # to get around thor showing klass name snake case in subcommand help (instead of subcommand name)
34
+ package_name "hive"
35
+ def self.banner(command, namespace = nil, subcommand = false)
36
+ "#{basename} #{@package_name} #{command.usage}"
37
+ end
38
+
39
+ desc "verify", "verifies hive tasks without loading them"
40
+ def verify
41
+ puts Zerg::Hive.verify
42
+ end
43
+
44
+ desc "list", "lists hive tasks"
45
+ def list
46
+ puts Zerg::Hive.list
47
+ end
48
+
49
+ desc "import [FILE]", "import a .ke file into the hive folder"
50
+ def import(file)
51
+ puts Zerg::Hive.import(file, options[:force])
52
+ end
53
+
54
+ desc "remove [TASK]", "remove a task from hive"
55
+ def remove(file)
56
+ puts Zerg::Hive.remove(file, options[:force])
57
+ end
58
+ end
59
+
60
+ class CLI < Thor
61
+ class_option :force, :type => :boolean, :banner => "force overwrite of files"
62
+ class_option :debug, :type => :boolean, :banner => "add debug option to driver"
63
+
64
+ def self.exit_on_failure?
65
+ true
66
+ end
67
+
68
+ desc "init", "initializes new hive"
69
+ def init
70
+ puts Zerg::Generators::HiveGen.start
71
+ end
72
+
73
+ desc "rush [TASK]", "runs a task from hive"
74
+ def rush(task)
75
+ puts Zerg::Runner.rush(task, options[:debug])
76
+ end
77
+
78
+ desc "clean [TASK]", "cleans a task"
79
+ def clean(task)
80
+ puts Zerg::Runner.clean(task, options[:debug])
81
+ end
82
+
83
+ register(HiveCLI, 'hive', 'hive [COMMAND]', 'Manage hive - a collection of task descriptions.')
84
+ end
85
+ end
@@ -0,0 +1,190 @@
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 'ostruct'
28
+ require 'securerandom'
29
+
30
+ module Zerg
31
+ class DriverRenderer
32
+ class Erbalize < OpenStruct
33
+ def self.erbalize_hash(template, sources)
34
+ Erbalize.new(sources).render(template)
35
+ end
36
+
37
+ def render(template)
38
+ ERB.new(template, nil, '-').result(binding)
39
+ end
40
+ end
41
+
42
+ # generate a virtualbox - compatible MAC address
43
+ def generateMACAddress()
44
+ firstChar = (0..255).map(&:chr).select{|x| x =~ /[0-9A-Fa-f]/}.sample(1).join
45
+ secondChar = (0..255).map(&:chr).select{|x| x =~ /[02468ACEace]/}.sample(1).join
46
+ restOfChars = (0..255).map(&:chr).select{|x| x =~ /[0-9A-Fa-f]/}.sample(10).join
47
+ return "#{firstChar}#{secondChar}#{restOfChars}"
48
+ end
49
+
50
+ def initialize(vm, name, instances, synced_folders, tasks )
51
+ @vm = vm
52
+ @name = name
53
+ @instances = instances
54
+ @tasks = tasks
55
+ @synced_folders = synced_folders
56
+ end
57
+
58
+ def render
59
+ puts ("Rendering driver templates...")
60
+
61
+ # TODO: generalize this processing better
62
+ abort("ERROR: Driver type '#{@vm["driver"]["drivertype"]} is not supported.") unless (@vm["driver"]["drivertype"] == "vagrant")
63
+
64
+ # load the template files
65
+ main_template = File.open(File.join("#{File.dirname(__FILE__)}", "..", "..", "data", "driver", "#{@vm["driver"]["drivertype"]}", "main.template"), 'r').read
66
+
67
+ # load the provider top level template
68
+ provider_parent_template = File.open(File.join("#{File.dirname(__FILE__)}", "..", "..", "data", "driver", "#{@vm["driver"]["drivertype"]}", "provider.template"), 'r').read
69
+
70
+ # load the machine details template
71
+ machine_template = File.open(File.join("#{File.dirname(__FILE__)}", "..", "..", "data", "driver", "#{@vm["driver"]["drivertype"]}", "machine.template"), 'r').read
72
+
73
+ # load the bridge details template
74
+ bridge_template = File.open(File.join("#{File.dirname(__FILE__)}", "..", "..", "data", "driver", "#{@vm["driver"]["drivertype"]}", "bridging.template"), 'r').read
75
+
76
+ # load the host only network details template
77
+ hostonly_template = File.open(File.join("#{File.dirname(__FILE__)}", "..", "..", "data", "driver", "#{@vm["driver"]["drivertype"]}", "hostonly.template"), 'r').read
78
+
79
+ # render templates....
80
+ # render provider details to string
81
+ #
82
+ # render provider details into a string
83
+ provider_details_array = @vm["driver"]["provider_options"]
84
+ provider_details = ""
85
+ for index in 0..provider_details_array.length - 1
86
+ provider_details += "\t\t" + provider_details_array[index] + "\n"
87
+ end
88
+
89
+ # render provider parent
90
+ sources = {
91
+ :provider => @vm["driver"]["providertype"],
92
+ :provider_specifics => provider_details
93
+ }
94
+ provider_parent_string = Erbalize.erbalize_hash(provider_parent_template, sources)
95
+
96
+ # render machine template
97
+ all_macs = Array.new
98
+ all_machines = ""
99
+ for index in 0..@instances - 1
100
+
101
+ # last ip octet offset for host only networking
102
+ ip_octet_offset = index
103
+
104
+ # inject randomized node_name into chef_client tasks
105
+ @tasks.each { |task|
106
+ if task["type"] == "chef_client"
107
+ task["node_name"] = "zergling_#{index}_#{SecureRandom.hex(20)}"
108
+ end
109
+ }
110
+
111
+ # tasks array rendered to ruby string. double encoding to escape quotes and allow for variable expansion
112
+ tasks_array = @tasks.to_json.to_json
113
+
114
+ # do we need the bridging template as well?
115
+ bridge_section = nil
116
+ if @vm.has_key?("bridge_description")
117
+ # mac address to use?
118
+ new_mac = ""
119
+ begin
120
+ new_mac = generateMACAddress()
121
+ end while all_macs.include? new_mac
122
+
123
+ sources = {
124
+ :machine_mac => new_mac,
125
+ :bridged_eth_description => @vm["bridge_description"]
126
+ }
127
+ bridge_section = Erbalize.erbalize_hash(bridge_template, sources)
128
+ end
129
+
130
+ # do we need the host only template as well?
131
+ hostonly_section = nil
132
+ if @vm["private_network"] == true
133
+ sources = {
134
+ :machine_name => "zergling_#{index}",
135
+ :last_octet => ip_octet_offset + 4, # TODO: this is probably specific to virtualbox networking
136
+ }
137
+ hostonly_section = Erbalize.erbalize_hash(hostonly_template, sources)
138
+ end
139
+
140
+ # blah
141
+ folder_definitions = nil
142
+ if @synced_folders != nil
143
+ folder_definitions = ""
144
+ @synced_folders.each { |folder|
145
+ other_options = ""
146
+ if folder.has_key?("options")
147
+ folder["options"].each { |option|
148
+ option.each do |key, value|
149
+ if value.is_a?(String)
150
+ other_options += ", :#{key} => \"#{value}\""
151
+ else
152
+ other_options += ", :#{key} => #{value}"
153
+ end
154
+ end
155
+ }
156
+ end
157
+
158
+ folder_definition = "zergling_#{index}.vm.synced_folder \"#{folder['host_path']}\", \"#{folder['guest_path']}\""
159
+ folder_definition = "#{folder_definition}#{other_options}" unless other_options.empty?()
160
+ folder_definitions += "\t\t#{folder_definition}\n"
161
+ }
162
+ end
163
+
164
+ sources = {
165
+ :machine_name => "zergling_#{index}",
166
+ :bridge_specifics => bridge_section,
167
+ :hostonly_specifics => hostonly_section,
168
+ :tasks_array => tasks_array,
169
+ :sync_folders_array => folder_definitions
170
+ }.delete_if { |k, v| v.nil? }
171
+
172
+ machine_section = Erbalize.erbalize_hash(machine_template, sources)
173
+ all_machines += "\n#{machine_section}"
174
+ end
175
+
176
+ sources = {
177
+ :provider_section => provider_parent_string,
178
+ :basebox_path => @vm["basebox"],
179
+ :box_name => "zergling_#{@name}_#{@vm["driver"]["providertype"]}",
180
+ :vm_defines => all_machines
181
+ }
182
+ full_template = Erbalize.erbalize_hash(main_template, sources)
183
+
184
+ # write the file
185
+ puts ("Writing #{File.join("#{Dir.pwd}", ".hive", "driver", @vm["driver"]["drivertype"], @name, "Vagrantfile")}...")
186
+ FileUtils.mkdir_p(File.join("#{Dir.pwd}", ".hive", "driver", @vm["driver"]["drivertype"], @name))
187
+ File.open(File.join("#{Dir.pwd}", ".hive", "driver", @vm["driver"]["drivertype"], @name, "Vagrantfile"), 'w') { |file| file.write(full_template) }
188
+ end
189
+ end
190
+ end