corl 0.4.0

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.
Files changed (132) hide show
  1. data/.document +5 -0
  2. data/.gitmodules +4 -0
  3. data/Gemfile +24 -0
  4. data/Gemfile.lock +123 -0
  5. data/LICENSE.txt +674 -0
  6. data/README.rdoc +27 -0
  7. data/Rakefile +78 -0
  8. data/VERSION +1 -0
  9. data/bin/corl +55 -0
  10. data/corl.gemspec +228 -0
  11. data/lib/corl/action/add.rb +69 -0
  12. data/lib/corl/action/bootstrap.rb +83 -0
  13. data/lib/corl/action/clone.rb +40 -0
  14. data/lib/corl/action/create.rb +55 -0
  15. data/lib/corl/action/exec.rb +41 -0
  16. data/lib/corl/action/extract.rb +49 -0
  17. data/lib/corl/action/image.rb +30 -0
  18. data/lib/corl/action/images.rb +55 -0
  19. data/lib/corl/action/lookup.rb +35 -0
  20. data/lib/corl/action/machines.rb +51 -0
  21. data/lib/corl/action/provision.rb +37 -0
  22. data/lib/corl/action/remove.rb +51 -0
  23. data/lib/corl/action/save.rb +53 -0
  24. data/lib/corl/action/seed.rb +115 -0
  25. data/lib/corl/action/spawn.rb +75 -0
  26. data/lib/corl/action/start.rb +37 -0
  27. data/lib/corl/action/stop.rb +30 -0
  28. data/lib/corl/action/update.rb +37 -0
  29. data/lib/corl/command/shell.rb +164 -0
  30. data/lib/corl/configuration/file.rb +386 -0
  31. data/lib/corl/event/puppet.rb +90 -0
  32. data/lib/corl/event/regex.rb +52 -0
  33. data/lib/corl/extension/puppetloader.rb +24 -0
  34. data/lib/corl/machine/fog.rb +310 -0
  35. data/lib/corl/machine/physical.rb +161 -0
  36. data/lib/corl/network/default.rb +26 -0
  37. data/lib/corl/node/aws.rb +90 -0
  38. data/lib/corl/node/fog.rb +198 -0
  39. data/lib/corl/node/google.rb +115 -0
  40. data/lib/corl/node/local.rb +26 -0
  41. data/lib/corl/node/rackspace.rb +89 -0
  42. data/lib/corl/project/git.rb +465 -0
  43. data/lib/corl/project/github.rb +108 -0
  44. data/lib/corl/provisioner/puppetnode/resource.rb +245 -0
  45. data/lib/corl/provisioner/puppetnode/resource_group.rb +205 -0
  46. data/lib/corl/provisioner/puppetnode.rb +407 -0
  47. data/lib/corl/template/environment.rb +73 -0
  48. data/lib/corl/template/json.rb +16 -0
  49. data/lib/corl/template/wrapper.rb +16 -0
  50. data/lib/corl/template/yaml.rb +16 -0
  51. data/lib/corl/translator/json.rb +27 -0
  52. data/lib/corl/translator/yaml.rb +27 -0
  53. data/lib/corl.rb +173 -0
  54. data/lib/corl_core/codes.rb +107 -0
  55. data/lib/corl_core/config/collection.rb +57 -0
  56. data/lib/corl_core/config/options.rb +70 -0
  57. data/lib/corl_core/config.rb +337 -0
  58. data/lib/corl_core/core.rb +59 -0
  59. data/lib/corl_core/corl.rb +254 -0
  60. data/lib/corl_core/errors.rb +84 -0
  61. data/lib/corl_core/facade.rb +126 -0
  62. data/lib/corl_core/gems.rb +72 -0
  63. data/lib/corl_core/manager.rb +425 -0
  64. data/lib/corl_core/mixin/action/commit.rb +58 -0
  65. data/lib/corl_core/mixin/action/keypair.rb +105 -0
  66. data/lib/corl_core/mixin/action/node.rb +129 -0
  67. data/lib/corl_core/mixin/action/project.rb +53 -0
  68. data/lib/corl_core/mixin/action/push.rb +52 -0
  69. data/lib/corl_core/mixin/config/collection.rb +53 -0
  70. data/lib/corl_core/mixin/config/ops.rb +53 -0
  71. data/lib/corl_core/mixin/config/options.rb +39 -0
  72. data/lib/corl_core/mixin/lookup.rb +196 -0
  73. data/lib/corl_core/mixin/macro/object_interface.rb +361 -0
  74. data/lib/corl_core/mixin/macro/plugin_interface.rb +380 -0
  75. data/lib/corl_core/mixin/settings.rb +46 -0
  76. data/lib/corl_core/mixin/sub_config.rb +148 -0
  77. data/lib/corl_core/mod/hash.rb +29 -0
  78. data/lib/corl_core/mod/hiera_backend.rb +63 -0
  79. data/lib/corl_core/plugin/action.rb +381 -0
  80. data/lib/corl_core/plugin/base.rb +374 -0
  81. data/lib/corl_core/plugin/command.rb +98 -0
  82. data/lib/corl_core/plugin/configuration.rb +177 -0
  83. data/lib/corl_core/plugin/event.rb +53 -0
  84. data/lib/corl_core/plugin/extension.rb +12 -0
  85. data/lib/corl_core/plugin/machine.rb +266 -0
  86. data/lib/corl_core/plugin/network.rb +359 -0
  87. data/lib/corl_core/plugin/node.rb +904 -0
  88. data/lib/corl_core/plugin/project.rb +927 -0
  89. data/lib/corl_core/plugin/provisioner.rb +51 -0
  90. data/lib/corl_core/plugin/template.rb +80 -0
  91. data/lib/corl_core/plugin/translator.rb +38 -0
  92. data/lib/corl_core/util/cli.rb +352 -0
  93. data/lib/corl_core/util/data.rb +404 -0
  94. data/lib/corl_core/util/disk.rb +114 -0
  95. data/lib/corl_core/util/git.rb +47 -0
  96. data/lib/corl_core/util/interface.rb +319 -0
  97. data/lib/corl_core/util/liquid.rb +17 -0
  98. data/lib/corl_core/util/package.rb +93 -0
  99. data/lib/corl_core/util/shell.rb +239 -0
  100. data/lib/corl_core/util/ssh.rb +286 -0
  101. data/lib/facter/corl_config_ready.rb +13 -0
  102. data/lib/facter/corl_exists.rb +15 -0
  103. data/lib/facter/corl_network.rb +17 -0
  104. data/lib/hiera/corl_logger.rb +18 -0
  105. data/lib/puppet/indirector/corl.rb +27 -0
  106. data/lib/puppet/indirector/data_binding/corl.rb +6 -0
  107. data/lib/puppet/parser/functions/config_initialized.rb +26 -0
  108. data/lib/puppet/parser/functions/corl_include.rb +44 -0
  109. data/lib/puppet/parser/functions/corl_resources.rb +58 -0
  110. data/lib/puppet/parser/functions/deep_merge.rb +21 -0
  111. data/lib/puppet/parser/functions/ensure.rb +29 -0
  112. data/lib/puppet/parser/functions/file_exists.rb +19 -0
  113. data/lib/puppet/parser/functions/global_array.rb +35 -0
  114. data/lib/puppet/parser/functions/global_hash.rb +35 -0
  115. data/lib/puppet/parser/functions/global_options.rb +23 -0
  116. data/lib/puppet/parser/functions/global_param.rb +43 -0
  117. data/lib/puppet/parser/functions/interpolate.rb +26 -0
  118. data/lib/puppet/parser/functions/is_false.rb +21 -0
  119. data/lib/puppet/parser/functions/is_true.rb +21 -0
  120. data/lib/puppet/parser/functions/module_array.rb +38 -0
  121. data/lib/puppet/parser/functions/module_hash.rb +38 -0
  122. data/lib/puppet/parser/functions/module_options.rb +23 -0
  123. data/lib/puppet/parser/functions/module_param.rb +48 -0
  124. data/lib/puppet/parser/functions/name.rb +21 -0
  125. data/lib/puppet/parser/functions/render.rb +33 -0
  126. data/lib/puppet/parser/functions/value.rb +21 -0
  127. data/locales/en.yml +232 -0
  128. data/spec/corl_core/interface_spec.rb +489 -0
  129. data/spec/corl_mock_input.rb +29 -0
  130. data/spec/corl_test_kernel.rb +22 -0
  131. data/spec/spec_helper.rb +15 -0
  132. metadata +406 -0
@@ -0,0 +1,465 @@
1
+
2
+ module CORL
3
+ module Project
4
+ class Git < Plugin::Project
5
+
6
+ #-----------------------------------------------------------------------------
7
+ # Project plugin interface
8
+
9
+ def normalize
10
+ super
11
+ end
12
+
13
+ #-----------------------------------------------------------------------------
14
+ # Git interface (local)
15
+
16
+ def ensure_git(reset = false)
17
+ if reset || @git_lib.nil?
18
+ @git_lib = nil
19
+
20
+ if directory.empty?
21
+ logger.warn("Can not manage Git project at #{directory} as it does not exist")
22
+ else
23
+ logger.debug("Ensuring Git instance to manage #{directory}")
24
+ @git_lib = Util::Git.new(directory)
25
+
26
+ if ! @git_lib.nil? && get(:create, false)
27
+ unless File.directory?(directory) && @git_lib.git.exist?
28
+ FileUtils.mkdir_p(directory) unless File.directory?(directory)
29
+ @git_lib.git.init({ :bare => false })
30
+ end
31
+ end
32
+ end
33
+ end
34
+ return myself
35
+ end
36
+ protected :ensure_git
37
+
38
+ #-----------------------------------------------------------------------------
39
+ # Checks
40
+
41
+ def can_persist?
42
+ ensure_git
43
+ return true unless @git_lib.nil?
44
+ return false
45
+ end
46
+
47
+ #---
48
+
49
+ def top?(path)
50
+ git_dir = File.join(path, '.git')
51
+ if File.exist?(git_dir)
52
+ return true if File.directory?(git_dir)
53
+ elsif File.exist?(path) && (path =~ /\.git$/ && File.exist?(File.join(path, 'HEAD')))
54
+ return true
55
+ end
56
+ return false
57
+ end
58
+
59
+ #---
60
+
61
+ def subproject?(path)
62
+ git_dir = File.join(path, '.git')
63
+ if File.exist?(git_dir)
64
+ unless File.directory?(git_dir)
65
+ git_dir = Util::Disk.read(git_dir)
66
+ unless git_dir.nil?
67
+ git_dir = git_dir.gsub(/^gitdir\:\s*/, '').strip
68
+ return true if File.directory?(git_dir)
69
+ end
70
+ end
71
+ end
72
+ return false
73
+ end
74
+
75
+ #---
76
+
77
+ def project_directory?(path, require_top_level = false)
78
+ path = File.expand_path(path)
79
+ git_dir = File.join(path, '.git')
80
+
81
+ if File.exist?(git_dir)
82
+ if File.directory?(git_dir)
83
+ return true
84
+ elsif ! require_top_level
85
+ git_dir = Util::Disk.read(git_dir)
86
+ unless git_dir.nil?
87
+ git_dir = git_dir.gsub(/^gitdir\:\s*/, '').strip
88
+ return true if File.directory?(git_dir)
89
+ end
90
+ end
91
+ elsif File.exist?(path) && (path =~ /\.git$/ && File.exist?(File.join(path, 'HEAD')))
92
+ return true
93
+ end
94
+ return false
95
+ end
96
+
97
+ #---
98
+
99
+ def new?(reset = false)
100
+ if get(:new, nil).nil? || reset
101
+ set(:new, git.native(:rev_parse, { :all => true }).empty?)
102
+ end
103
+ get(:new, false)
104
+ end
105
+
106
+ #-----------------------------------------------------------------------------
107
+ # Property accessors / modifiers
108
+
109
+ def lib
110
+ return @git_lib
111
+ end
112
+
113
+ #---
114
+
115
+ def git
116
+ return lib.git if can_persist?
117
+ return nil
118
+ end
119
+ protected :git
120
+
121
+ #---
122
+
123
+ def set_location(directory)
124
+ super do
125
+ ensure_git(true)
126
+ end
127
+ return myself
128
+ end
129
+
130
+ #---
131
+
132
+ def config(name, options = {})
133
+ return super do |config|
134
+ git.config(config.export, name)
135
+ end
136
+ end
137
+
138
+ #---
139
+
140
+ def set_config(name, value, options = {})
141
+ return super do |config, processed_value|
142
+ git.config(config.export, name, processed_value)
143
+ end
144
+ end
145
+
146
+ #---
147
+
148
+ def delete_config(name, options = {})
149
+ return super do |config|
150
+ git.config(config.import({ :remove_section => true }).export, name)
151
+ end
152
+ end
153
+
154
+ #---
155
+
156
+ def subproject_config(options = {})
157
+ return super do |config|
158
+ result = {}
159
+
160
+ if new?
161
+ logger.debug("Project has no sub project configuration yet (has not been committed to)")
162
+ else
163
+ commit = lib.commit(revision)
164
+ blob = commit.tree/'.gitmodules' unless commit.nil?
165
+
166
+ if blob
167
+ logger.debug("Houston, we have a Git blob!")
168
+
169
+ lines = blob.data.gsub(/\r\n?/, "\n" ).split("\n")
170
+ current = nil
171
+
172
+ lines.each do |line|
173
+ if line =~ /^\[submodule "(.+)"\]$/
174
+ current = $1
175
+ result[current] = {}
176
+ result[current]['id'] = (commit.tree/current).id
177
+
178
+ logger.debug("Reading: #{current}")
179
+
180
+ elsif line =~ /^\t(\w+) = (.+)$/
181
+ result[current][$1] = $2
182
+ result[current]['id'] = (commit.tree/$2).id if $1 == 'path'
183
+ end
184
+ end
185
+ end
186
+ end
187
+ result
188
+ end
189
+ end
190
+
191
+ #-----------------------------------------------------------------------------
192
+ # Basic Git operations
193
+
194
+ def load_revision
195
+ return super do
196
+ if new?
197
+ logger.debug("Project has no current revision yet (has not been committed to)")
198
+ nil
199
+
200
+ else
201
+ current_revision = git.native(:rev_parse, { :abbrev_ref => true }, 'HEAD').strip
202
+
203
+ logger.debug("Current revision: #{current_revision}")
204
+ current_revision
205
+ end
206
+ end
207
+ end
208
+
209
+ #---
210
+
211
+ def checkout(revision)
212
+ return super do |success|
213
+ if new?
214
+ logger.debug("Project can not be checked out (has not been committed to)")
215
+ else
216
+ unless lib.bare
217
+ success = safe_exec(false) do
218
+ git.checkout({ :raise => true }, revision)
219
+ end
220
+ end
221
+ end
222
+ success
223
+ end
224
+ end
225
+
226
+ #---
227
+
228
+ def commit(files = '.', options = {})
229
+ return super do |config, time, user, message|
230
+ safe_exec(false) do
231
+ git.reset({}, 'HEAD') # Clear the index so we get a clean commit
232
+
233
+ files = array(files)
234
+
235
+ logger.debug("Adding files to Git index")
236
+
237
+ git.add({ :raise => true }, files) # Get all added and updated files
238
+ git.add({ :update => true, :raise => true }, files) # Get all deleted files
239
+
240
+ commit_options = {
241
+ :raise => true,
242
+ :m => "#{time} by <#{user}> - #{message}",
243
+ :allow_empty => config.get(:allow_empty, false)
244
+ }
245
+ commit_options[:author] = config[:author] if config.get(:author, false)
246
+
247
+ logger.debug("Composing commit options: #{commit_options.inspect}")
248
+ git.commit(commit_options)
249
+
250
+ new?(true)
251
+ end
252
+ end
253
+ end
254
+
255
+ #-----------------------------------------------------------------------------
256
+ # Subproject operations
257
+
258
+ def load_subprojects(options = {})
259
+ return super do |project_path, data|
260
+ File.exist?(File.join(project_path, '.git'))
261
+ end
262
+ end
263
+
264
+ #---
265
+
266
+ def add_subproject(path, url, revision, options = {})
267
+ return super do |config|
268
+ safe_exec(false) do
269
+ branch_options = ''
270
+ branch_options = [ '-b', config[:revision] ] if config.get(:revision, false)
271
+
272
+ path = config[:path]
273
+ url = config[:url]
274
+
275
+ git.submodule({ :raise => true }, 'add', *branch_options, url, path)
276
+
277
+ config.set(:files, [ '.gitmodules', path ])
278
+ end
279
+ end
280
+ end
281
+
282
+ #---
283
+
284
+ def delete_subproject(path)
285
+ return super do |config|
286
+ safe_exec(false) do
287
+ path = config[:path]
288
+ submodule_key = "submodule.#{path}"
289
+
290
+ logger.debug("Deleting Git configurations for #{submodule_key}")
291
+ delete_config(submodule_key)
292
+ delete_config(submodule_key, { :file => '.gitmodules' })
293
+
294
+ logger.debug("Cleaning Git index cache for #{path}")
295
+ git.rm({ :cached => true }, path)
296
+
297
+ logger.debug("Removing Git submodule directories")
298
+ FileUtils.rm_rf(File.join(directory, path))
299
+ FileUtils.rm_rf(File.join(git.git_dir, 'modules', path))
300
+
301
+ config.set(:files, [ '.gitmodules', path ])
302
+ end
303
+ end
304
+ end
305
+
306
+ #---
307
+
308
+ def update_subprojects
309
+ return super do
310
+ safe_exec(false) do
311
+ git.submodule({ :raise => true, :timeout => false }, 'update', '--init', '--recursive')
312
+ end
313
+ end
314
+ end
315
+
316
+ #-----------------------------------------------------------------------------
317
+ # Remote operations
318
+
319
+ def init_remotes
320
+ return super do
321
+ origin_url = config('remote.origin.url').strip
322
+
323
+ logger.debug("Original origin remote url: #{origin_url}")
324
+ origin_url
325
+ end
326
+ end
327
+
328
+ #---
329
+
330
+ def remote(name)
331
+ return super do
332
+ url = config("remote.#{name}.url").strip
333
+ url.empty? ? nil : url
334
+ end
335
+ end
336
+
337
+ #---
338
+
339
+ def set_remote(name, url)
340
+ return super do |processed_url|
341
+ safe_exec(false) do
342
+ git.remote({ :raise => true }, 'add', name.to_s, processed_url)
343
+ end
344
+ end
345
+ end
346
+
347
+ #---
348
+
349
+ def add_remote_url(name, url, options = {})
350
+ return super do |config, processed_url|
351
+ safe_exec(false) do
352
+ git.remote({
353
+ :raise => true,
354
+ :add => true,
355
+ :delete => config.get(:delete, false),
356
+ :push => config.get(:push, false)
357
+ }, 'set-url', name.to_s, processed_url)
358
+ end
359
+ end
360
+ end
361
+
362
+ #---
363
+
364
+ def delete_remote(name)
365
+ return super do
366
+ if config("remote.#{name}.url").empty?
367
+ logger.debug("Project can not delete remote #{name} because it does not exist yet")
368
+ true
369
+ else
370
+ safe_exec(false) do
371
+ git.remote({ :raise => true }, 'rm', name.to_s)
372
+ end
373
+ end
374
+ end
375
+ end
376
+
377
+ #---
378
+
379
+ def syncronize(cloud, options = {})
380
+ return super do |config|
381
+ config.init(:remote_path, '/var/git')
382
+ config.set(:add, true)
383
+ end
384
+ end
385
+
386
+ #-----------------------------------------------------------------------------
387
+ # SSH operations
388
+
389
+ def pull(remote = :origin, options = {})
390
+ return super do |config, processed_remote|
391
+ flags = []
392
+ flags << :tags if config.get(:tags, true)
393
+
394
+ result = CORL.command({
395
+ :command => :git,
396
+ :data => { 'git-dir=' => git.git_dir },
397
+ :subcommand => {
398
+ :command => :pull,
399
+ :flags => flags,
400
+ :args => [ processed_remote, config.get(:revision, get(:revision, :master)) ]
401
+ }
402
+ }, config.get(:provider, :shell)).exec(config) do |op, command, data|
403
+ block_given? ? yield(op, command, data) : true
404
+ end
405
+
406
+ if result.status == CORL.code.success
407
+ new?(true)
408
+ true
409
+ else
410
+ false
411
+ end
412
+ end
413
+ end
414
+
415
+ #---
416
+
417
+ def push(remote = :edit, options = {})
418
+ return super do |config, processed_remote|
419
+ push_branch = config.get(:revision, '')
420
+
421
+ flags = []
422
+ flags << :all if push_branch.empty?
423
+ flags << :tags if ! push_branch.empty? && config.get(:tags, true)
424
+
425
+ result = CORL.command({
426
+ :command => :git,
427
+ :data => { 'git-dir=' => git.git_dir },
428
+ :subcommand => {
429
+ :command => :push,
430
+ :flags => flags,
431
+ :args => [ processed_remote, push_branch ]
432
+ }
433
+ }, config.get(:provider, :shell)).exec(config) do |op, command, data|
434
+ block_given? ? yield(op, command, data) : true
435
+ end
436
+
437
+ result.status == CORL.code.success
438
+ end
439
+ end
440
+
441
+ #-----------------------------------------------------------------------------
442
+ # Utilities
443
+
444
+ def translate_url(host, path, options = {})
445
+ return super do |config|
446
+ user = config.get(:user, 'git')
447
+ auth = config.get(:auth, true)
448
+
449
+ user + (auth ? '@' : '://') + host + (auth ? ':' : '/') + path
450
+ end
451
+ end
452
+
453
+ #---
454
+
455
+ def translate_edit_url(url, options = {})
456
+ return super do |config|
457
+ if matches = url.strip.match(/^(https?|git)\:\/\/([^\/]+)\/(.+)/)
458
+ protocol, host, path = matches.captures
459
+ translate_url(host, path, config.import({ :auth => true }))
460
+ end
461
+ end
462
+ end
463
+ end
464
+ end
465
+ end
@@ -0,0 +1,108 @@
1
+
2
+ corl_require(File.dirname(__FILE__), :git)
3
+
4
+ #---
5
+
6
+ module CORL
7
+ module Project
8
+ class Github < Git
9
+
10
+ #-----------------------------------------------------------------------------
11
+ # Project plugin interface
12
+
13
+ def normalize
14
+ if reference = delete(:reference, nil)
15
+ myself.name = reference
16
+ else
17
+ if url = get(:url, nil)
18
+ myself.name = url
19
+ set(:url, myself.class.expand_url(url, get(:ssh, false)))
20
+ end
21
+ end
22
+ super
23
+ end
24
+
25
+ #---
26
+
27
+ def set_connection
28
+ require 'octokit'
29
+
30
+ @client = Octokit::Client.new :netrc => true
31
+ @client.login
32
+ end
33
+
34
+ #-----------------------------------------------------------------------------
35
+ # Property accessor / modifiers
36
+
37
+ def client
38
+ set_connection unless @client
39
+ @client
40
+ end
41
+
42
+ #-----------------------------------------------------------------------------
43
+ # Project operations
44
+
45
+ def init_auth
46
+ super do
47
+ key_id = ENV['USER'] + '@' + lookup(:ipaddress)
48
+ ssh_key = public_key_str
49
+
50
+ if private_key && ssh_key
51
+ begin
52
+ deploy_keys = client.deploy_keys(self.name)
53
+ github_id = nil
54
+ keys_match = true
55
+
56
+ deploy_keys.each do |key_resource|
57
+ if key_resource.title == key_id
58
+ github_id = key_resource.id
59
+ keys_match = false if key_resource.key != ssh_key
60
+ break
61
+ end
62
+ end
63
+
64
+ if github_id
65
+ unless keys_match
66
+ client.edit_deploy_key(self.name, github_id, { :key => ssh_key })
67
+ verify_key
68
+ end
69
+ else
70
+ client.add_deploy_key(self.name, key_id, ssh_key)
71
+ verify_key
72
+ end
73
+
74
+ rescue Exception => error
75
+ logger.error(error.inspect)
76
+ logger.error(error.message)
77
+ logger.error(Util::Data.to_yaml(error.backtrace))
78
+
79
+ ui.error(error.message, { :prefix => false }) if error.message
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ #-----------------------------------------------------------------------------
86
+ # Utilities
87
+
88
+ def self.expand_url(path, editable = false)
89
+ if editable
90
+ protocol = 'git@'
91
+ separator = ':'
92
+ else
93
+ protocol = 'https://'
94
+ separator = '/'
95
+ end
96
+ return "#{protocol}github.com#{separator}" + path + '.git'
97
+ end
98
+
99
+ #---
100
+
101
+ def verify_key
102
+ Util::SSH.init_session('github.com', 'git', 22, private_key)
103
+ Util::SSH.close('github.com', 'git')
104
+ end
105
+ protected :verify_key
106
+ end
107
+ end
108
+ end