cheffish 1.4.1 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +201 -201
  3. data/README.md +120 -120
  4. data/Rakefile +23 -23
  5. data/lib/chef/provider/chef_acl.rb +439 -439
  6. data/lib/chef/provider/chef_client.rb +53 -53
  7. data/lib/chef/provider/chef_container.rb +55 -55
  8. data/lib/chef/provider/chef_data_bag.rb +55 -55
  9. data/lib/chef/provider/chef_data_bag_item.rb +278 -278
  10. data/lib/chef/provider/chef_environment.rb +83 -83
  11. data/lib/chef/provider/chef_group.rb +83 -83
  12. data/lib/chef/provider/chef_mirror.rb +169 -169
  13. data/lib/chef/provider/chef_node.rb +87 -87
  14. data/lib/chef/provider/chef_organization.rb +155 -155
  15. data/lib/chef/provider/chef_resolved_cookbooks.rb +46 -46
  16. data/lib/chef/provider/chef_role.rb +84 -84
  17. data/lib/chef/provider/chef_user.rb +59 -59
  18. data/lib/chef/provider/private_key.rb +225 -225
  19. data/lib/chef/provider/public_key.rb +88 -88
  20. data/lib/chef/resource/chef_acl.rb +69 -69
  21. data/lib/chef/resource/chef_client.rb +48 -48
  22. data/lib/chef/resource/chef_container.rb +22 -22
  23. data/lib/chef/resource/chef_data_bag.rb +22 -22
  24. data/lib/chef/resource/chef_data_bag_item.rb +121 -121
  25. data/lib/chef/resource/chef_environment.rb +77 -77
  26. data/lib/chef/resource/chef_group.rb +53 -53
  27. data/lib/chef/resource/chef_mirror.rb +52 -52
  28. data/lib/chef/resource/chef_node.rb +22 -22
  29. data/lib/chef/resource/chef_organization.rb +69 -69
  30. data/lib/chef/resource/chef_resolved_cookbooks.rb +35 -35
  31. data/lib/chef/resource/chef_role.rb +110 -110
  32. data/lib/chef/resource/chef_user.rb +56 -56
  33. data/lib/chef/resource/private_key.rb +48 -48
  34. data/lib/chef/resource/public_key.rb +25 -25
  35. data/lib/cheffish/actor_provider_base.rb +131 -131
  36. data/lib/cheffish/basic_chef_client.rb +184 -184
  37. data/lib/cheffish/chef_provider_base.rb +246 -246
  38. data/lib/cheffish/chef_run.rb +162 -162
  39. data/lib/cheffish/chef_run_data.rb +19 -19
  40. data/lib/cheffish/chef_run_listener.rb +30 -30
  41. data/lib/cheffish/key_formatter.rb +113 -113
  42. data/lib/cheffish/merged_config.rb +94 -94
  43. data/lib/cheffish/recipe_dsl.rb +157 -157
  44. data/lib/cheffish/rspec/chef_run_support.rb +83 -83
  45. data/lib/cheffish/rspec/matchers/be_idempotent.rb +16 -16
  46. data/lib/cheffish/rspec/matchers/emit_no_warnings_or_errors.rb +15 -15
  47. data/lib/cheffish/rspec/matchers/have_updated.rb +37 -37
  48. data/lib/cheffish/rspec/matchers/partially_match.rb +63 -63
  49. data/lib/cheffish/rspec/matchers.rb +4 -4
  50. data/lib/cheffish/rspec/recipe_run_wrapper.rb +78 -59
  51. data/lib/cheffish/rspec/repository_support.rb +108 -108
  52. data/lib/cheffish/rspec.rb +8 -8
  53. data/lib/cheffish/server_api.rb +52 -52
  54. data/lib/cheffish/version.rb +3 -3
  55. data/lib/cheffish/with_pattern.rb +21 -21
  56. data/lib/cheffish.rb +235 -235
  57. data/spec/functional/fingerprint_spec.rb +64 -64
  58. data/spec/functional/merged_config_spec.rb +19 -19
  59. data/spec/functional/server_api_spec.rb +13 -13
  60. data/spec/integration/chef_acl_spec.rb +879 -879
  61. data/spec/integration/chef_client_spec.rb +105 -105
  62. data/spec/integration/chef_container_spec.rb +33 -33
  63. data/spec/integration/chef_group_spec.rb +309 -309
  64. data/spec/integration/chef_mirror_spec.rb +491 -491
  65. data/spec/integration/chef_node_spec.rb +786 -786
  66. data/spec/integration/chef_organization_spec.rb +226 -226
  67. data/spec/integration/chef_role_spec.rb +78 -78
  68. data/spec/integration/chef_user_spec.rb +85 -85
  69. data/spec/integration/private_key_spec.rb +399 -399
  70. data/spec/integration/recipe_dsl_spec.rb +28 -28
  71. data/spec/integration/rspec/converge_spec.rb +183 -183
  72. data/spec/support/key_support.rb +29 -29
  73. data/spec/support/spec_support.rb +15 -15
  74. data/spec/unit/get_private_key_spec.rb +131 -131
  75. data/spec/unit/recipe_run_wrapper_spec.rb +37 -37
  76. metadata +3 -2
@@ -1,162 +1,162 @@
1
- require 'cheffish/basic_chef_client'
2
-
3
- module Cheffish
4
- class ChefRun
5
- #
6
- # @param chef_config A hash with symbol keys that looks suspiciously similar to `Chef::Config`.
7
- # Some possible options:
8
- # - stdout: <IO object> - where to stream stdout to
9
- # - stderr: <IO object> - where to stream stderr to
10
- # - log_level: :debug|:info|:warn|:error|:fatal
11
- # - log_location: <path|IO object> - where to stream logs to
12
- # - verbose_logging: true|false - true if you want verbose logging in :debug
13
- #
14
- def initialize(chef_config={})
15
- @chef_config = chef_config || {}
16
- end
17
-
18
- attr_reader :chef_config
19
-
20
- class StringIOTee < StringIO
21
- def initialize(*streams)
22
- super()
23
- @streams = streams.flatten.select { |s| !s.nil? }
24
- end
25
-
26
- attr_reader :streams
27
-
28
- def write(*args, &block)
29
- super
30
- streams.each { |s| s.write(*args, &block) }
31
- end
32
- end
33
-
34
- def client
35
- @client ||= begin
36
- chef_config = self.chef_config.dup
37
- chef_config[:log_level] ||= :debug if !chef_config.has_key?(:log_level)
38
- chef_config[:verbose_logging] = false if !chef_config.has_key?(:verbose_logging)
39
- chef_config[:stdout] = StringIOTee.new(chef_config[:stdout])
40
- chef_config[:stderr] = StringIOTee.new(chef_config[:stderr])
41
- chef_config[:log_location] = StringIOTee.new(chef_config[:log_location])
42
- @client = ::Cheffish::BasicChefClient.new(nil,
43
- [ event_sink, Chef::Formatters.new(:doc, chef_config[:stdout], chef_config[:stderr]) ],
44
- chef_config
45
- )
46
- end
47
- end
48
-
49
- def event_sink
50
- @event_sink ||= EventSink.new
51
- end
52
-
53
- #
54
- # output
55
- #
56
- def stdout
57
- @client ? client.chef_config[:stdout].string : nil
58
- end
59
- def stderr
60
- @client ? client.chef_config[:stderr].string : nil
61
- end
62
- def logs
63
- @client ? client.chef_config[:log_location].string : nil
64
- end
65
- def logged_warnings
66
- logs.lines.select { |l| l =~ /^\[[^\]]*\] WARN:/ }.join("\n")
67
- end
68
- def logged_errors
69
- logs.lines.select { |l| l =~ /^\[[^\]]*\] ERROR:/ }.join("\n")
70
- end
71
- def logged_info
72
- logs.lines.select { |l| l =~ /^\[[^\]]*\] INFO:/ }.join("\n")
73
- end
74
-
75
- def resources
76
- client.resource_collection
77
- end
78
-
79
- def compile_recipe(&recipe)
80
- client.load_block(&recipe)
81
- end
82
-
83
- def converge
84
- begin
85
- client.converge
86
- @converged = true
87
- rescue RuntimeError => e
88
- @raised_exception = e
89
- raise
90
- end
91
- end
92
-
93
- def reset
94
- @client = nil
95
- @converged = nil
96
- @stdout = nil
97
- @stderr = nil
98
- @logs = nil
99
- @raised_exception = nil
100
- end
101
-
102
- def converged?
103
- !!@converged
104
- end
105
-
106
- def converge_failed?
107
- @raised_exception.nil? ? false : true
108
- end
109
-
110
- def updated?
111
- client.updated?
112
- end
113
-
114
- def up_to_date?
115
- !client.updated?
116
- end
117
-
118
- def output_for_failure_message
119
- message = ""
120
- if stdout && !stdout.empty?
121
- message << "--- ---\n"
122
- message << "--- Chef Client Output ---\n"
123
- message << "--- ---\n"
124
- message << stdout
125
- message << "\n" if !stdout.end_with?("\n")
126
- end
127
- if stderr && !stderr.empty?
128
- message << "--- ---\n"
129
- message << "--- Chef Client Error Output ---\n"
130
- message << "--- ---\n"
131
- message << stderr
132
- message << "\n" if !stderr.end_with?("\n")
133
- end
134
- if logs && !logs.empty?
135
- message << "--- ---\n"
136
- message << "--- Chef Client Logs ---\n"
137
- message << "--- ---\n"
138
- message << logs
139
- end
140
- message
141
- end
142
-
143
- class EventSink
144
- def initialize
145
- @events = []
146
- end
147
-
148
- attr_reader :events
149
-
150
- def method_missing(method, *args)
151
- @events << [ method, *args ]
152
- end
153
-
154
- def respond_to_missing?(method_name, include_private = false)
155
- # Chef::EventDispatch::Dispatcher calls #respond_to? to see (basically) if we'll accept an event;
156
- # obviously, per above #method_missing, we'll accept whatever we're given. if there's a problem, it
157
- # will surface higher up the stack.
158
- true
159
- end
160
- end
161
- end
162
- end
1
+ require 'cheffish/basic_chef_client'
2
+
3
+ module Cheffish
4
+ class ChefRun
5
+ #
6
+ # @param chef_config A hash with symbol keys that looks suspiciously similar to `Chef::Config`.
7
+ # Some possible options:
8
+ # - stdout: <IO object> - where to stream stdout to
9
+ # - stderr: <IO object> - where to stream stderr to
10
+ # - log_level: :debug|:info|:warn|:error|:fatal
11
+ # - log_location: <path|IO object> - where to stream logs to
12
+ # - verbose_logging: true|false - true if you want verbose logging in :debug
13
+ #
14
+ def initialize(chef_config={})
15
+ @chef_config = chef_config || {}
16
+ end
17
+
18
+ attr_reader :chef_config
19
+
20
+ class StringIOTee < StringIO
21
+ def initialize(*streams)
22
+ super()
23
+ @streams = streams.flatten.select { |s| !s.nil? }
24
+ end
25
+
26
+ attr_reader :streams
27
+
28
+ def write(*args, &block)
29
+ super
30
+ streams.each { |s| s.write(*args, &block) }
31
+ end
32
+ end
33
+
34
+ def client
35
+ @client ||= begin
36
+ chef_config = self.chef_config.dup
37
+ chef_config[:log_level] ||= :debug if !chef_config.has_key?(:log_level)
38
+ chef_config[:verbose_logging] = false if !chef_config.has_key?(:verbose_logging)
39
+ chef_config[:stdout] = StringIOTee.new(chef_config[:stdout])
40
+ chef_config[:stderr] = StringIOTee.new(chef_config[:stderr])
41
+ chef_config[:log_location] = StringIOTee.new(chef_config[:log_location])
42
+ @client = ::Cheffish::BasicChefClient.new(nil,
43
+ [ event_sink, Chef::Formatters.new(:doc, chef_config[:stdout], chef_config[:stderr]) ],
44
+ chef_config
45
+ )
46
+ end
47
+ end
48
+
49
+ def event_sink
50
+ @event_sink ||= EventSink.new
51
+ end
52
+
53
+ #
54
+ # output
55
+ #
56
+ def stdout
57
+ @client ? client.chef_config[:stdout].string : nil
58
+ end
59
+ def stderr
60
+ @client ? client.chef_config[:stderr].string : nil
61
+ end
62
+ def logs
63
+ @client ? client.chef_config[:log_location].string : nil
64
+ end
65
+ def logged_warnings
66
+ logs.lines.select { |l| l =~ /^\[[^\]]*\] WARN:/ }.join("\n")
67
+ end
68
+ def logged_errors
69
+ logs.lines.select { |l| l =~ /^\[[^\]]*\] ERROR:/ }.join("\n")
70
+ end
71
+ def logged_info
72
+ logs.lines.select { |l| l =~ /^\[[^\]]*\] INFO:/ }.join("\n")
73
+ end
74
+
75
+ def resources
76
+ client.resource_collection
77
+ end
78
+
79
+ def compile_recipe(&recipe)
80
+ client.load_block(&recipe)
81
+ end
82
+
83
+ def converge
84
+ begin
85
+ client.converge
86
+ @converged = true
87
+ rescue RuntimeError => e
88
+ @raised_exception = e
89
+ raise
90
+ end
91
+ end
92
+
93
+ def reset
94
+ @client = nil
95
+ @converged = nil
96
+ @stdout = nil
97
+ @stderr = nil
98
+ @logs = nil
99
+ @raised_exception = nil
100
+ end
101
+
102
+ def converged?
103
+ !!@converged
104
+ end
105
+
106
+ def converge_failed?
107
+ @raised_exception.nil? ? false : true
108
+ end
109
+
110
+ def updated?
111
+ client.updated?
112
+ end
113
+
114
+ def up_to_date?
115
+ !client.updated?
116
+ end
117
+
118
+ def output_for_failure_message
119
+ message = ""
120
+ if stdout && !stdout.empty?
121
+ message << "--- ---\n"
122
+ message << "--- Chef Client Output ---\n"
123
+ message << "--- ---\n"
124
+ message << stdout
125
+ message << "\n" if !stdout.end_with?("\n")
126
+ end
127
+ if stderr && !stderr.empty?
128
+ message << "--- ---\n"
129
+ message << "--- Chef Client Error Output ---\n"
130
+ message << "--- ---\n"
131
+ message << stderr
132
+ message << "\n" if !stderr.end_with?("\n")
133
+ end
134
+ if logs && !logs.empty?
135
+ message << "--- ---\n"
136
+ message << "--- Chef Client Logs ---\n"
137
+ message << "--- ---\n"
138
+ message << logs
139
+ end
140
+ message
141
+ end
142
+
143
+ class EventSink
144
+ def initialize
145
+ @events = []
146
+ end
147
+
148
+ attr_reader :events
149
+
150
+ def method_missing(method, *args)
151
+ @events << [ method, *args ]
152
+ end
153
+
154
+ def respond_to_missing?(method_name, include_private = false)
155
+ # Chef::EventDispatch::Dispatcher calls #respond_to? to see (basically) if we'll accept an event;
156
+ # obviously, per above #method_missing, we'll accept whatever we're given. if there's a problem, it
157
+ # will surface higher up the stack.
158
+ true
159
+ end
160
+ end
161
+ end
162
+ end
@@ -1,19 +1,19 @@
1
- require 'chef/config'
2
- require 'cheffish/with_pattern'
3
-
4
- module Cheffish
5
- class ChefRunData
6
- def initialize(config)
7
- @local_servers = []
8
- @current_chef_server = Cheffish.default_chef_server(config)
9
- end
10
-
11
- extend Cheffish::WithPattern
12
- with :data_bag
13
- with :environment
14
- with :data_bag_item_encryption
15
- with :chef_server
16
-
17
- attr_reader :local_servers
18
- end
19
- end
1
+ require 'chef/config'
2
+ require 'cheffish/with_pattern'
3
+
4
+ module Cheffish
5
+ class ChefRunData
6
+ def initialize(config)
7
+ @local_servers = []
8
+ @current_chef_server = Cheffish.default_chef_server(config)
9
+ end
10
+
11
+ extend Cheffish::WithPattern
12
+ with :data_bag
13
+ with :environment
14
+ with :data_bag_item_encryption
15
+ with :chef_server
16
+
17
+ attr_reader :local_servers
18
+ end
19
+ end
@@ -1,30 +1,30 @@
1
- require 'chef/event_dispatch/base'
2
-
3
- module Cheffish
4
- class ChefRunListener < Chef::EventDispatch::Base
5
- def initialize(node)
6
- @node = node
7
- end
8
-
9
- attr_reader :node
10
-
11
- def run_complete(node)
12
- disconnect
13
- end
14
-
15
- def run_failed(exception)
16
- disconnect
17
- end
18
-
19
- private
20
-
21
- def disconnect
22
- # Stop the servers
23
- if node.run_context
24
- node.run_context.cheffish.local_servers.each do |server|
25
- server.stop
26
- end
27
- end
28
- end
29
- end
30
- end
1
+ require 'chef/event_dispatch/base'
2
+
3
+ module Cheffish
4
+ class ChefRunListener < Chef::EventDispatch::Base
5
+ def initialize(node)
6
+ @node = node
7
+ end
8
+
9
+ attr_reader :node
10
+
11
+ def run_complete(node)
12
+ disconnect
13
+ end
14
+
15
+ def run_failed(exception)
16
+ disconnect
17
+ end
18
+
19
+ private
20
+
21
+ def disconnect
22
+ # Stop the servers
23
+ if node.run_context
24
+ node.run_context.cheffish.local_servers.each do |server|
25
+ server.stop
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,113 +1,113 @@
1
- require 'openssl'
2
- require 'net/ssh'
3
- require 'etc'
4
- require 'socket'
5
- require 'digest/md5'
6
- require 'base64'
7
-
8
- module Cheffish
9
- class KeyFormatter
10
- # Returns nil or key, format
11
- def self.decode(str, pass_phrase=nil, filename='')
12
- key_format = {}
13
- key_format[:format] = format_of(str)
14
-
15
- case key_format[:format]
16
- when :openssh
17
- key = decode_openssh_key(str, filename)
18
- else
19
- begin
20
- key = OpenSSL::PKey.read(str) { pass_phrase }
21
- rescue
22
- return nil
23
- end
24
- end
25
-
26
- key_format[:type] = type_of(key)
27
- key_format[:size] = size_of(key) if size_of(key)
28
- key_format[:pass_phrase] = pass_phrase if pass_phrase
29
- # TODO cipher, exponent
30
-
31
- [key, key_format]
32
- end
33
-
34
- def self.encode(key, key_format)
35
- format = key_format[:format] || :pem
36
- case format
37
- when :openssh
38
- encode_openssh_key(key)
39
- when :pem
40
- if key_format[:pass_phrase]
41
- cipher = key_format[:cipher] || 'DES-EDE3-CBC'
42
- key.to_pem(OpenSSL::Cipher.new(cipher), key_format[:pass_phrase])
43
- else
44
- key.to_pem
45
- end
46
- when :der
47
- key.to_der
48
- when :fingerprint, :pkcs1md5fingerprint
49
- hexes = Digest::MD5.hexdigest(key.to_der)
50
- # Put : between every pair of hexes
51
- hexes.scan(/../).join(':')
52
- when :rfc4716md5fingerprint
53
- type, base64_data, etc = encode_openssh_key(key).split
54
- data = Base64.decode64(base64_data)
55
- hexes = Digest::MD5.hexdigest(data)
56
- hexes.scan(/../).join(':')
57
- when :pkcs8sha1fingerprint
58
- if RUBY_VERSION.to_f >= 2.0
59
- raise "PKCS8 SHA1 not supported in Ruby #{RUBY_VERSION}"
60
- end
61
- require 'openssl_pkcs8'
62
- pkcs8_pem = key.to_pem_pkcs8
63
- pkcs8_base64 = pkcs8_pem.split("\n").reject { |l| l =~ /^-----/ }
64
- pkcs8_data = Base64.decode64(pkcs8_base64.join)
65
- hexes = Digest::SHA1.hexdigest(pkcs8_data)
66
- hexes.scan(/../).join(':')
67
- else
68
- raise "Unrecognized key format #{format}"
69
- end
70
- end
71
-
72
- private
73
-
74
- def self.encode_openssh_key(key)
75
- # TODO there really isn't a method somewhere in net/ssh or openssl that does this??
76
- type = key.ssh_type
77
- data = [ key.to_blob ].pack('m0')
78
- "#{type} #{data} #{Etc.getlogin}@#{Socket.gethostname}"
79
- end
80
-
81
- def self.decode_openssh_key(str, filename='')
82
- Net::SSH::KeyFactory.load_data_public_key(str, filename)
83
- end
84
-
85
- def self.format_of(key_contents)
86
- if key_contents.start_with?('-----BEGIN ')
87
- :pem
88
- elsif key_contents.start_with?('ssh-rsa ') || key_contents.start_with?('ssh-dss ')
89
- :openssh
90
- else
91
- :der
92
- end
93
- end
94
-
95
- def self.type_of(key)
96
- case key.class
97
- when OpenSSL::PKey::RSA
98
- :rsa
99
- when OpenSSL::PKey::DSA
100
- :dsa
101
- end
102
- end
103
-
104
- def self.size_of(key)
105
- case key.class
106
- when OpenSSL::PKey::RSA
107
- key.n.num_bytes * 8
108
- else
109
- nil
110
- end
111
- end
112
- end
113
- end
1
+ require 'openssl'
2
+ require 'net/ssh'
3
+ require 'etc'
4
+ require 'socket'
5
+ require 'digest/md5'
6
+ require 'base64'
7
+
8
+ module Cheffish
9
+ class KeyFormatter
10
+ # Returns nil or key, format
11
+ def self.decode(str, pass_phrase=nil, filename='')
12
+ key_format = {}
13
+ key_format[:format] = format_of(str)
14
+
15
+ case key_format[:format]
16
+ when :openssh
17
+ key = decode_openssh_key(str, filename)
18
+ else
19
+ begin
20
+ key = OpenSSL::PKey.read(str) { pass_phrase }
21
+ rescue
22
+ return nil
23
+ end
24
+ end
25
+
26
+ key_format[:type] = type_of(key)
27
+ key_format[:size] = size_of(key) if size_of(key)
28
+ key_format[:pass_phrase] = pass_phrase if pass_phrase
29
+ # TODO cipher, exponent
30
+
31
+ [key, key_format]
32
+ end
33
+
34
+ def self.encode(key, key_format)
35
+ format = key_format[:format] || :pem
36
+ case format
37
+ when :openssh
38
+ encode_openssh_key(key)
39
+ when :pem
40
+ if key_format[:pass_phrase]
41
+ cipher = key_format[:cipher] || 'DES-EDE3-CBC'
42
+ key.to_pem(OpenSSL::Cipher.new(cipher), key_format[:pass_phrase])
43
+ else
44
+ key.to_pem
45
+ end
46
+ when :der
47
+ key.to_der
48
+ when :fingerprint, :pkcs1md5fingerprint
49
+ hexes = Digest::MD5.hexdigest(key.to_der)
50
+ # Put : between every pair of hexes
51
+ hexes.scan(/../).join(':')
52
+ when :rfc4716md5fingerprint
53
+ type, base64_data, etc = encode_openssh_key(key).split
54
+ data = Base64.decode64(base64_data)
55
+ hexes = Digest::MD5.hexdigest(data)
56
+ hexes.scan(/../).join(':')
57
+ when :pkcs8sha1fingerprint
58
+ if RUBY_VERSION.to_f >= 2.0
59
+ raise "PKCS8 SHA1 not supported in Ruby #{RUBY_VERSION}"
60
+ end
61
+ require 'openssl_pkcs8'
62
+ pkcs8_pem = key.to_pem_pkcs8
63
+ pkcs8_base64 = pkcs8_pem.split("\n").reject { |l| l =~ /^-----/ }
64
+ pkcs8_data = Base64.decode64(pkcs8_base64.join)
65
+ hexes = Digest::SHA1.hexdigest(pkcs8_data)
66
+ hexes.scan(/../).join(':')
67
+ else
68
+ raise "Unrecognized key format #{format}"
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def self.encode_openssh_key(key)
75
+ # TODO there really isn't a method somewhere in net/ssh or openssl that does this??
76
+ type = key.ssh_type
77
+ data = [ key.to_blob ].pack('m0')
78
+ "#{type} #{data} #{Etc.getlogin}@#{Socket.gethostname}"
79
+ end
80
+
81
+ def self.decode_openssh_key(str, filename='')
82
+ Net::SSH::KeyFactory.load_data_public_key(str, filename)
83
+ end
84
+
85
+ def self.format_of(key_contents)
86
+ if key_contents.start_with?('-----BEGIN ')
87
+ :pem
88
+ elsif key_contents.start_with?('ssh-rsa ') || key_contents.start_with?('ssh-dss ')
89
+ :openssh
90
+ else
91
+ :der
92
+ end
93
+ end
94
+
95
+ def self.type_of(key)
96
+ case key.class
97
+ when OpenSSL::PKey::RSA
98
+ :rsa
99
+ when OpenSSL::PKey::DSA
100
+ :dsa
101
+ end
102
+ end
103
+
104
+ def self.size_of(key)
105
+ case key.class
106
+ when OpenSSL::PKey::RSA
107
+ key.n.num_bytes * 8
108
+ else
109
+ nil
110
+ end
111
+ end
112
+ end
113
+ end