mccloud 0.0.2 → 0.0.3

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.
data/.gitignore CHANGED
@@ -1,5 +1,9 @@
1
- Mccloudfile
2
- .mccloud
3
- *.pp
4
- puppet/*
5
- chef/*
1
+ /Mccloudfile
2
+ /.mccloud
3
+ /*.pp
4
+ /puppet/*
5
+ /chef/*
6
+ /.fog
7
+ /bootstrap*
8
+ Mccloudfile.*
9
+ pkg/*
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mccloud (0.0.2)
4
+ mccloud (0.0.3)
5
5
  cucumber (= 0.8.5)
6
6
  fog
7
7
  highline (~> 1.6.1)
@@ -18,100 +18,109 @@ class MccloudCLI < Thor
18
18
 
19
19
  end
20
20
 
21
- desc "init", "creates a Mccloud Config File"
21
+ desc "init", "Creates a Mccloud Config File"
22
22
  method_options :force => :boolean
23
23
  def init(amiId=nil)
24
24
  Mccloud::Command.init(amiId,options)
25
25
  end
26
26
 
27
- desc "up", "bring a machine up"
27
+ desc "up [NAME]", "Fires up a machine, bootstrap and provision"
28
28
  method_options :force => :boolean
29
29
  def up(selection=nil)
30
30
  create_session
31
31
  @session.up(selection,options)
32
32
  end
33
33
 
34
- desc "status", "show a status of all machines that are configured"
34
+ desc "status", "Show a status of all machines that are configured"
35
35
  method_options :verbose => false
36
36
  def status(selection=nil)
37
37
  create_session
38
38
  @session.status(selection,options)
39
39
  end
40
40
 
41
- desc "halt [NAME]", "shutdown the machine"
41
+ desc "halt [NAME]", "Shutdown the machine"
42
42
  method_options :force => :boolean
43
43
  def halt(selection=nil)
44
44
  create_session
45
45
  @session.halt(selection,options)
46
46
  end
47
47
 
48
- desc "ssh [NAME]", "spawns an ssh to the machine"
48
+ desc "ssh [NAME]", "Spawns an ssh client to the machine"
49
49
  method_options :screen => :boolean
50
50
  def ssh(selection=nil,command=nil)
51
51
  create_session
52
52
  @session.ssh(selection,command,options)
53
53
  end
54
54
 
55
- desc "suspend", "freeze"
55
+ desc "suspend [NAME]", "Shutdown of the machine"
56
56
  method_options :force => :boolean
57
57
  def suspend(selection=nil)
58
58
  create_session
59
59
  @session.suspend(selection,options)
60
60
  end
61
61
 
62
- desc "boot", "freeze"
63
- method_options :force => :boolean
64
- def boot(selection=nil)
65
- create_session
66
- @session.boot(selection,options)
67
- end
62
+ # desc "boot", "same a"
63
+ # method_options :force => :boolean
64
+ # def boot(selection=nil)
65
+ # create_session
66
+ # @session.boot(selection,options)
67
+ # end
68
68
 
69
- desc "destroy [NAME]", "destroys the machine"
69
+ desc "destroy [NAME]", "Destroys the machine"
70
70
  method_options :force => :boolean
71
71
  def destroy(selection=nil)
72
72
  create_session
73
73
  @session.destroy(selection,options)
74
74
  end
75
75
 
76
- desc "reload [NAME]", "reloads the machine"
76
+ desc "reload [NAME]", "Reboots the machine"
77
77
  method_options :force => :boolean
78
78
  def reload(selection=nil?)
79
79
  create_session
80
80
  @session.reload(selection,options)
81
81
  end
82
82
 
83
- desc "command [NAME] [COMMAND]", "exec a command on a box"
84
- method_options :parallel => :boolean
85
- def command(selection=nil,command="who am i")
86
- create_session
87
- @session.command(selection,command,options)
88
- end
83
+ # desc "command [NAME] [COMMAND]", "exec a command on a box"
84
+ # method_options :parallel => :boolean
85
+ # def command(selection=nil,command="who am i")
86
+ # create_session
87
+ # @session.command(selection,command,options)
88
+ # end
89
89
 
90
- desc "bootstrap [NAME]", "exec a command on a box"
90
+ desc "bootstrap [NAME]", "Executes the bootstrap sequence"
91
91
  method_options :sudo => :boolean
92
92
  def bootstrap(selection=nil,script="who am i")
93
93
  create_session
94
94
  @session.bootstrap(selection,script,options)
95
95
  end
96
96
 
97
- desc "multi","multi"
98
- method_options :verbose => :boolean
99
- def multi(selection=nil,command="who am i")
100
- create_session
101
- @session.multi(selection,command,options)
102
- end
97
+ # desc "multi","multi"
98
+ # method_options :verbose => :boolean
99
+ # def multi(selection=nil,command="who am i")
100
+ # create_session
101
+ # @session.multi(selection,command,options)
102
+ # end
103
103
 
104
- desc "provision", "provision the machine"
104
+ desc "provision [NAME]", "Runs the provisioner the machine"
105
105
  def provision(selection=nil)
106
106
  create_session
107
107
  @session.provision(selection,options)
108
108
  end
109
109
 
110
- desc "server", "runs a server+ forwards"
110
+ desc "server", "Runs a server+ forwards network ports"
111
111
  def server(selection=nil)
112
112
  create_session
113
113
  @session.server(selection,options)
114
114
  end
115
115
  end
116
116
 
117
- MccloudCLI.start
117
+ puts
118
+ 80.times { |i| printf "*" } ; puts
119
+ myversion="* Mccloud v#{Mccloud::VERSION} - alpha - use at your own risk"
120
+ puts "#{myversion}"
121
+ 80.times { |i| printf "*" } ; puts
122
+ puts
123
+
124
+
125
+ MccloudCLI.start
126
+ puts
@@ -25,7 +25,7 @@ module Mccloud
25
25
  #options={ :port => 22, :keys => [ vm.key ], :paranoid => false, :keys_only => true}
26
26
  #Mccloud::Ssh.execute(instance.public_ip_address,vm.user,options,"sudo /tmp/bootstrap.sh")
27
27
  end
28
- multi(selection,"sudo /tmp/bootstrap.sh",options)
28
+ multi(selection,"sudo /tmp/bootstrap.sh",options.merge({ "sudo" => true}))
29
29
  end
30
30
  end
31
31
  end
@@ -4,14 +4,14 @@ module Mccloud
4
4
  module Command
5
5
  def destroy(selection=nil,options=nil)
6
6
  on_selected_machines(selection) do |id,vm|
7
- unless vm.instance.state == "shutting-down" || vm.instance.state =="terminated"
8
- puts "Destroying #{vm.name} with id - #{id}"
7
+ unless vm.instance.nil? || vm.instance.state == "shutting-down" || vm.instance.state =="terminated"
8
+ puts "Destroying machine #{vm.name} (#{id})"
9
9
  vm.instance.destroy
10
10
 
11
11
  vm.instance.wait_for { print "."; STDOUT.flush; state=="terminated"}
12
12
  puts
13
13
  else
14
- puts "Server #{vm.name} - Id: #{id} has state #{vm.instance.state} so not destroying it "
14
+ puts "Machine #{vm.name} is already terminated"
15
15
  end
16
16
  end
17
17
  end
@@ -1,9 +1,509 @@
1
1
  require 'mccloud/generators'
2
+ require 'highline/import'
3
+ require 'mccloud/util/sshkey'
4
+
5
+ require 'erb'
2
6
 
3
7
  module Mccloud
4
8
  module Command
5
- def self.init(amiId=nil,options=nil)
6
- Mccloud::Generators.run_cli Dir.pwd, File.basename(__FILE__), Mccloud::VERSION, ARGV
9
+ include Mccloud::Util
10
+ def self.init(amiId=nil,options=nil)
11
+
12
+ trap("INT") { puts
13
+ puts ""; exit }
14
+
15
+ init_options=ARGV
16
+
17
+ puts "Welcome to the Mccloud configurator: "
18
+ init_options<< "--mcPrefix"
19
+ init_options<< "mccloud"
20
+
21
+ init_options<< "--providerId"
22
+ init_options<< "AWS"
23
+
24
+ create_fog('AWS')
25
+
26
+ template=ERB.new File.new("lib/mccloud/templates/Mccloudfile.erb").read,nil,"%"
27
+ mcPrefix="mccloud"
28
+ providerId="AWS"
29
+ mcEnvironment=select_environment
30
+ mcIdentity=select_identity
31
+
32
+ serverName=select_servername
33
+
34
+ #strip all
35
+ serverName.gsub!(/[^[:alnum:]]/, '_')
36
+
37
+ image_selection=select_image
38
+ imageId=image_selection['imageId']
39
+ userName=image_selection['userName']
40
+ imageDescription=image_selection['imageDescription']
41
+ arch=image_selection['arch']
42
+
43
+ full_bootstrapScript=image_selection['bootstrap']
44
+ copy_bootstrap(full_bootstrapScript)
45
+ bootstrapScript=File.basename("#{full_bootstrapScript}")
46
+
47
+ availabilityZone=select_zone
48
+ flavorId=select_flavor(arch)
49
+
50
+ fullIdentity=Array.new
51
+ if !mcPrefix.nil?
52
+ fullIdentity << mcPrefix
53
+ end
54
+ if mcEnvironment!=""
55
+ fullIdentity << mcEnvironment
56
+ end
57
+ if mcIdentity!=""
58
+ fullIdentity << mcIdentity
59
+ end
60
+ full_identity=fullIdentity.join("-")
61
+
62
+ securityGroup=select_security_group(full_identity)
63
+
64
+ keypair_selection=select_keypair
65
+ keyName=keypair_selection['keyName']
66
+ privateKeyPath=keypair_selection['privateKeyPath']
67
+ publicKeyPath=keypair_selection['publicKeyPath']
68
+
69
+ confirmed=false
70
+ content=template.result(binding)
71
+ if File.exists?("Mccloudfile")
72
+ confirmed=agree("\nDo you want to overwrite your existing config?: ") { |q| q.default="no"}
73
+ else
74
+ confirmed=true
75
+ end
76
+ if confirmed
77
+ puts
78
+ puts "Writing your config file Mccloudfile"
79
+ mccloudfile=File.new("Mccloudfile","w")
80
+ mccloudfile.puts(content)
81
+ mccloudfile.close
82
+ else
83
+ puts "Ok did not overwrite the config, moving along"
84
+ end
85
+
86
+
87
+ #Mccloud::Generators.run_cli Dir.pwd, File.basename(__FILE__), Mccloud::VERSION, init_options
88
+ end
89
+
90
+ private
91
+
92
+ def self.copy_bootstrap(filename=nil)
93
+
94
+ unless filename.nil?
95
+
96
+ FileUtils.cp(filename,File.basename(filename))
97
+ puts "Copied bootstrap - #{File.basename(filename)} to your current directory"
98
+ end
99
+ end
100
+
101
+ def self.generate_key(comment=nil)
102
+ keyset=SSHKey.generate(:comment => comment)
103
+ return keyset
104
+ end
105
+ def self.select_image()
106
+
107
+ # Canonical
108
+ # http://uec-images.ubuntu.com/releases/10.04/release/
109
+ # http://jonathanhui.com/create-and-launch-amazon-ec2-instance-ubuntu-and-centos
110
+
111
+ suggestions=[
112
+ { "Name" => "Ubuntu 10.10 - Maverick 64-bit (Canonical/EBS)", "ImageId" => "ami-cef405a7",
113
+ "UserName" => "ubuntu" ,"Arch" => "64", "Bootstraps" =>
114
+ [ {"Description" => "Ruby via standard .deb packages + rubygems from source + (chef,puppet) as gems", "Filename" => "bootstrap-ubuntu-system.sh"},
115
+ {"Description" => "Ruby 1.8.7 via rvm + (chef,puppet) as gems", "Filename" => "bootstrap-ubuntu-rvm-1.8.7.sh"},
116
+ {"Description" => "Empty bootstrap you can customize", "Filename" => "bootstrap-custom.sh"},
117
+ ]
118
+ },
119
+ { "Name" => "Ubuntu 10.10 - Maverick 32-bit (Canonical/EBS)", "ImageId" => "ami-ccf405a5",
120
+ "UserName" => "ubuntu" ,"Arch" => "32", "Bootstraps" =>
121
+ [ {"Description" => "Ruby via standard .deb packages + rubygems from source + (chef,puppet) as gems", "Filename" => "bootstrap-ubuntu-system.sh"},
122
+ {"Description" => "Ruby 1.8.7 via rvm + (chef,puppet) as gems", "Filename" => "bootstrap-ubuntu-rvm-1.8.7.sh"},
123
+ {"Description" => "Empty bootstrap you can customize", "Filename" => "bootstrap-custom.sh"},
124
+
125
+ ]},
126
+ { "Name" => "Ubuntu 10.04 - Lucid 64-bit (Canonical/EBS)", "ImageId" => "ami-3202f25b",
127
+ "UserName" => "ubuntu" ,"Arch" => "64", "Bootstraps" =>
128
+ [ {"Description" => "Ruby via standard .deb packages + rubygems from source + (chef,puppet) as gems", "Filename" => "bootstrap-ubuntu-system.sh"},
129
+ {"Description" => "Ruby 1.8.7 via rvm + (chef,puppet) as gems", "Filename" => "bootstrap-ubuntu-rvm-1.8.7.sh"},
130
+ {"Description" => "Empty bootstrap you can customize", "Filename" => "bootstrap-custom.sh"},
131
+
132
+ ]},
133
+ { "Name" => "Ubuntu 10.04 - Lucid 32-bit (Canonical/EBS)", "ImageId" => "ami-3e02f257",
134
+ "UserName" => "ubuntu" ,"Arch" => "32", "Bootstraps" =>
135
+ [ {"Description" => "Ruby via standard .deb packages + rubygems from source + (chef,puppet) as gems", "Filename" => "bootstrap-ubuntu-system.sh"},
136
+ {"Description" => "Ruby 1.8.7 via rvm + (chef,puppet) as gems", "Filename" => "bootstrap-ubuntu-rvm-1.8.7.sh"},
137
+ {"Description" => "Empty bootstrap you can customize", "Filename" => "bootstrap-custom.sh"},
138
+
139
+ ]},
140
+ { "Name" => "Centos 5.4 - 64-bit (Rightscale/EBS)", "ImageId" => "ami-4d42a924",
141
+ "UserName" => "root" ,"Arch" => "64", "Bootstraps" =>
142
+ [ {"Description" => "Ruby 1.8.7 from source + (chef,puppet) as gems", "Filename" => "bootstrap-centos-rubysource-1.8.7.sh"},
143
+ {"Description" => "Ruby 1.8.7 via rvm + (chef,puppet) as gems", "Filename" => "bootstrap-centos-rvm-1.8.7.sh"},
144
+ {"Description" => "Ruby REE 1.8.7 via rvm + (chef,puppet) as gems", "Filename" => "bootstrap-centos-rvm-ree-1.8.7.sh"},
145
+
146
+ {"Description" => "Ruby 1.9.2 via rvm + (chef,puppet) as gems", "Filename" => "bootstrap-centos-rvm-1.9.2.sh"},
147
+ {"Description" => "Empty bootstrap you can customize", "Filename" => "bootstrap-custom.sh"},
148
+
149
+ ]},
150
+ { "Name" => "Centos 5.4 - 32-bit (Rightscale/EBS)", "ImageId" => "ami-2342a94a",
151
+ "UserName" => "root" ,"Arch" => "32", "Bootstraps" =>
152
+ [ {"Description" => "Ruby 1.8.7 from source + (chef,puppet) as gems", "Filename" => "bootstrap-centos-rubysource-1.8.7.sh"},
153
+ {"Description" => "Ruby 1.8.7 via rvm + (chef,puppet) as gems", "Filename" => "bootstrap-centos-rvm-1.8.7.sh"},
154
+ {"Description" => "Ruby REE 1.8.7 via rvm + (chef,puppet) as gems", "Filename" => "bootstrap-centos-rvm-ree-1.8.7.sh"},
155
+
156
+ {"Description" => "Ruby 1.9.2 via rvm + (chef,puppet) as gems", "Filename" => "bootstrap-centos-rvm-1.9.2.sh"},
157
+ {"Description" => "Empty bootstrap you can customize", "Filename" => "bootstrap-custom.sh"},
158
+
159
+ ]},
160
+
161
+ ]
162
+
163
+ imageId=""
164
+ userName="root"
165
+ imageDescription=""
166
+ arch="0"
167
+ bootstrap=""
168
+
169
+ choose do |menu|
170
+ menu.index_suffix =") "
171
+ menu.header="\nSuggested image Ids"
172
+ menu.default="1"
173
+ menu.prompt="\nSelect the Image Id (aka AMI-ID on EC2): |1| "
174
+ suggestions.each do |suggestion|
175
+ menu.choice "#{suggestion['Name']} - #{suggestion['ImageId']}" do
176
+ imageId=suggestion['ImageId']
177
+ userName=suggestion['UserName']
178
+ imageDescription=suggestion['Name']
179
+ arch=suggestion['Arch']
180
+
181
+ choose do |bootstrap_menu|
182
+ bootstrap_menu.index_suffix =") "
183
+ bootstrap_menu.header="\nSuggested bootstraps"
184
+ bootstrap_menu.default="1"
185
+ bootstrap_menu.prompt="\nSelect the Image Id (aka AMI-ID on EC2): |1| "
186
+ suggestion["Bootstraps"].each do |bootstrap_file|
187
+ bootstrap_menu.choice "#{bootstrap_file['Description']}" do
188
+ bootstrap=File.expand_path(File.join(File.dirname(__FILE__),'..','templates',"#{bootstrap_file["Filename"]}"))
189
+ end
190
+ end
191
+ end
192
+
193
+ end
194
+ end
195
+ menu.choice "Specify your own:" do
196
+ imageId=ask("Image Id: ")
197
+ userName=ask("Username to login: ")
198
+ imageDescription=ask("Description: ")
199
+ arch=ask("Architecture (32,64): "){|q| q.default="64"}
200
+ bootstrap=ask("Path to Bootstrap script: ")
201
+ end
202
+
203
+ end
204
+
205
+ return { "imageId"=>imageId,"userName" =>userName,"imageDescription" => imageDescription,"arch" => arch,"bootstrap" => bootstrap}
206
+
207
+ end
208
+
209
+ def self.select_identity()
210
+
211
+ puts "If you share the same cloud provider account, you can specify an identity"
212
+ puts "to uniquely identify *your* servers (ex. firstname.lastname)"
213
+ puts "Leave blank if you want to work in a team "
214
+ puts ""
215
+ mcIdentity=ask("Identity: ") {|q| q.default="#{ENV['USER']}"}
216
+ puts
217
+ return mcIdentity
218
+
219
+ end
220
+
221
+ def self.select_servername()
222
+
223
+ puts "Provide an short name to identify your server (ex. web,db) "
224
+ mcIdentity=ask("Servername: ") { |q| q.default="web"}
225
+ puts
226
+ return mcIdentity
227
+
228
+ end
229
+
230
+ def self.select_environment()
231
+
232
+ puts "Provide a name for the environment your are using (ex. development, test, demo)"
233
+ mcEnvironment=ask("Environment: "){|q| q.default="development"}
234
+ puts
235
+ return mcEnvironment
236
+
237
+ end
238
+
239
+
240
+ def self.select_flavor(filter=nil)
241
+ flavorId="t1.micro"
242
+ choose do |menu|
243
+ menu.index_suffix =") "
244
+ menu.header="\nAvailable machine flavors"
245
+ menu.default="1"
246
+ menu.prompt="\nSelect the flavor for your machine: |1| "
247
+ @provider.flavors.each do |flavor|
248
+ if "#{flavor.bits}"==filter || "#{flavor.bits}"=="0"
249
+ menu.choice "#{flavor.name} - #{flavor.id} - #{flavor.cores} cores" do
250
+ flavorId=flavor.id
251
+ end
252
+ end
253
+ end
254
+ end
255
+ return flavorId
256
+
257
+ end
258
+
259
+ def self.select_security_group(identity=nil)
260
+
261
+ securityGroup="default"
262
+ choose do |menu|
263
+ menu.index_suffix =") "
264
+ menu.header="\nAvailable security groups"
265
+ menu.default="1"
266
+ menu.prompt="\nSelect the security group for your machine: |1| "
267
+
268
+ mcgroup=@provider.security_groups.get("#{identity}-securitygroup")
269
+ if mcgroup.nil?
270
+ menu.choice "[Create new] #{identity}-securitygroup" do
271
+ sg=@provider.security_groups.new
272
+ sg.name="#{identity}-securitygroup"
273
+ sg.description="#{identity}-securitygroup"
274
+ sg.save
275
+
276
+ puts "Authorizing access to port 22"
277
+ sg.authorize_port_range(22..22)
278
+
279
+ securityGroup="#{identity}-securitygroup"
280
+ end
281
+ else
282
+ menu.choice "#{identity}-securitygroup" do
283
+ securityGroup="#{identity}-securitygroup"
284
+ end
285
+ end
286
+
287
+ @provider.security_groups.each do |group|
288
+ unless "#{group.name}"=="#{identity}-securitygroup"
289
+ menu.choice "#{group.name} - #{group.description}" do
290
+ puts "Make sure this security group has ssh/port 22 enabled\n"
291
+ securityGroup=group.name
292
+ end
293
+ end
294
+ end
295
+ end
296
+
297
+ # We should check to see if 22 is enabled for that zone
298
+ #(AWS.security_groups.get("JMETER").ip_permissions[0]["fromPort"]..AWS.security_groups.get("JMETER").ip_permissions[0]["toPort"]) === 22
299
+ # make sure port 22 is open in the first security group
300
+ # security_group = connection.security_groups.get(server.groups.first)
301
+ # authorized = security_group.ip_permissions.detect do |ip_permission|
302
+ # ip_permission['ipRanges'].first && ip_permission['ipRanges'].first['cidrIp'] == '0.0.0.0/0' &&
303
+ # ip_permission['fromPort'] == 22 &&
304
+ # ip_permission['ipProtocol'] == 'tcp' &&
305
+ # ip_permission['toPort'] == 22
306
+ # end
307
+ # unless authorized
308
+ # security_group.authorize_port_range(22..22)
309
+ # end
310
+ return securityGroup
311
+
312
+ end
313
+
314
+ def self.select_zone()
315
+
316
+
317
+ zone="default"
318
+
319
+ choose do |menu|
320
+ menu.index_suffix =") "
321
+ menu.header="\nAvailable zones"
322
+ menu.default="us-east-1b"
323
+ menu.prompt="\nSelect the zone for your machine: |#{menu.default}| "
324
+
325
+ @provider.describe_availability_zones.body["availabilityZoneInfo"].each do |region|
326
+ menu.choice "#{region['zoneName']} - #{region['regionName']}" do
327
+ zone=region['zoneName']
328
+ end
329
+ end
330
+
331
+ [ {"zoneName" => "eu-west-1a", "regionName" => "eu-west-1"}, {"zoneName" => "eu-west-1b", "regionName" => "eu-west-1"}].each do |region|
332
+ menu.choice "#{region['zoneName']} - #{region['regionName']}" do
333
+ zone=region['zoneName']
334
+ end
335
+ end
336
+ end
337
+
338
+ return zone
339
+
340
+ end
341
+
342
+ def self.create_fog(provider='AWS')
343
+ begin
344
+ @provider=Fog::Compute.new(:provider => provider)
345
+ rescue ArgumentError => e
346
+ # Missing required arguments:
347
+ required_string=e.message
348
+ required_string["Missing required arguments: "]=""
349
+ required_options=required_string.split(", ")
350
+ puts "Please provide credentials for provider [#{provider}]:"
351
+ answer=Hash.new
352
+ for fog_option in required_options do
353
+ answer["#{fog_option}".to_sym]=ask("- #{fog_option}: ")
354
+ #{ |q| q.validate = /\A\d{5}(?:-?\d{4})?\Z/ }
355
+ end
356
+ puts "\nThe following snippet will be written to #{File.join(ENV['HOME'],".fog")}"
357
+
358
+ snippet=":default:\n"
359
+ for fog_option in required_options do
360
+ snippet=snippet+" :#{fog_option}: #{answer[fog_option.to_sym]}\n"
361
+ end
362
+
363
+ puts "======== snippit start ====="
364
+ puts "#{snippet}"
365
+ puts "======== snippit end ======="
366
+ confirmed=agree("Do you want to save this?: ") {|q| q.default="yes"}
367
+ puts
368
+
369
+ if (confirmed)
370
+ fogfile=File.new("#{File.join(ENV['HOME'],".fog")}","w")
371
+ FileUtils.chmod(0600,fogfile)
372
+ fogfile.puts "#{snippet}"
373
+ fogfile.close
374
+ else
375
+ #puts "Ok, we won't write it, but we continue with your credentials in memory"
376
+ exit -1
377
+ end
378
+ begin
379
+ answer[:provider]= provider
380
+ @provider=Fog::Compute.new(answer)
381
+ rescue
382
+ puts "We tried to create the provider but failed again, sorry we give up"
383
+ exit -1
384
+ end
385
+ end
386
+ end
387
+
388
+ def self.select_keypair()
389
+
390
+
391
+ valid_private_key=false
392
+ valid_public_key=false
393
+ private_key_path=""
394
+ public_key_path=""
395
+
396
+ keyName=nil
397
+ choose do |menu|
398
+ menu.index_suffix =") "
399
+ menu.header="\nAvailable keypairs"
400
+ menu.default="1"
401
+ menu.prompt="\nSelect a keypair: |1| "
402
+
403
+ menu.choice "Use/Generate a new (mccloud) keypair (RSA)" do
404
+
405
+ private_key_path=File.join("#{ENV['HOME']}",".ssh","mccloud_rsa")
406
+ public_key_path=File.join("#{ENV['HOME']}",".ssh","mccloud_rsa.pub")
407
+
408
+ if File.exists?(private_key_path)
409
+ reuse=agree("\nReuse existing (mccloud) keypair?: ") { |q| q.default="yes"}
410
+ else
411
+ reuse=false
412
+ end
413
+
414
+ rsa_key=nil
415
+ unless reuse
416
+ keyName=ask("\nPlease enter a name for your new key: "){|q| q.default="mccloud-key-#{ENV['USER']}"}
417
+ rsa_key=generate_key(keyName)
418
+
419
+ unless File.exists?(File.dirname(public_key_path))
420
+ puts "Creating directory #{File.dirname(public_key_path)}"
421
+ FileUtils.mkdir_p(File.dirname(public_key_path))
422
+ FileUtils.chmod(0700,File.dirname(public_key_path))
423
+ end
424
+
425
+ unless File.exists?(File.dirname(private_key_path))
426
+ puts "Creating directory #{File.dirname(private_key_path)}"
427
+
428
+ FileUtils.mkdir_p(File.dirname(private_key_path))
429
+ FileUtils.chmod(0700,File.dirname(private_key_path))
430
+ end
431
+
432
+
433
+ File.open(public_key_path, 'w') {|f| f.write(rsa_key.ssh_public_key) }
434
+ File.open(private_key_path, 'w') {|f| f.write(rsa_key.rsa_private_key) }
435
+ FileUtils.chmod(0600,private_key_path)
436
+ FileUtils.chmod(0600,public_key_path)
437
+
438
+ puts
439
+ puts "Created a new public key in #{public_key_path}"
440
+ puts "Created a new private key in #{private_key_path}"
441
+ puts "I suggest you backup these later."
442
+ puts
443
+
444
+ provider_keypair=@provider.key_pairs.get(keyName)
445
+ unless (provider_keypair.nil?)
446
+ puts "Updating your existing key #{keyName} with cloud provider"
447
+ provider_keypair.destroy()
448
+ end
449
+ provider_keypair=@provider.key_pairs.create(
450
+ :name => keyName,
451
+ :public_key => rsa_key.ssh_public_key )
452
+ puts "Registered #{keyName} with your cloud provider"
453
+
454
+ else
455
+ keyName=ask("\nPlease enter a name for your key: "){|q| q.default="mccloud-key-#{ENV['USER']}"}
456
+ end
457
+
458
+ valid_public_key=true
459
+ valid_private_key=true
460
+ end
461
+
462
+ menu.choice "Provide your own keypair (RSA)" do
463
+ keyName=nil
464
+ end
465
+ @provider.key_pairs.each do |keypair|
466
+ menu.choice "#{keypair.name} - #{keypair.fingerprint}" do
467
+ keyName=keypair.name
468
+ end
469
+ end
470
+ end
471
+
472
+
473
+ if keyName.nil?
474
+ puts "You selected to provide a custom keypair. Note that only RSA keys are supported"
475
+ puts
476
+ keyName=ask("Please enter a name for your custom key: (ex. ec2key-firstname.lastname) "){|q| q.default="mccloud-key-#{ENV['USER']}"}
477
+ else
478
+ valid_public_key=true
479
+ end
480
+
481
+ while (valid_private_key==false) do
482
+ private_key_path=ask("Enter full path to private key: "){|q| q.default="#{File.join(ENV['HOME'],'.ssh','mccloud-id_rsa')}"}
483
+ if File.exists?(private_key_path)
484
+ valid_private_key=true
485
+ else
486
+ puts "#{private_key_path} does not exist"
487
+ end
488
+ end
489
+
490
+ while (valid_public_key==false) do
491
+ public_key_path=ask("Enter full path to public key: "){|q| q.default="#{File.join(ENV['HOME'],'.ssh','mccloud-id_rsa.pub')}"}
492
+ if File.exists?(public_key_path)
493
+ valid_public_key=true
494
+ else
495
+ puts "#{public_key_path} does not exist"
496
+ end
497
+ end
498
+
499
+
500
+ if private_key_path==""
501
+ return {"keyName" => keyName,"privateKeyPath" => private_key_path}
502
+
503
+ else
504
+ return {"keyName" => keyName,"publicKeyPath" => public_key_path,"privateKeyPath" => private_key_path}
505
+ end
506
+ end
507
+
7
508
  end
8
509
  end
9
- end