leap_cli 1.2.5

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 (72) hide show
  1. data/bin/leap +81 -0
  2. data/lib/core_ext/boolean.rb +14 -0
  3. data/lib/core_ext/hash.rb +35 -0
  4. data/lib/core_ext/json.rb +42 -0
  5. data/lib/core_ext/nil.rb +5 -0
  6. data/lib/core_ext/string.rb +14 -0
  7. data/lib/leap/platform.rb +52 -0
  8. data/lib/leap_cli/commands/ca.rb +430 -0
  9. data/lib/leap_cli/commands/clean.rb +16 -0
  10. data/lib/leap_cli/commands/compile.rb +134 -0
  11. data/lib/leap_cli/commands/deploy.rb +172 -0
  12. data/lib/leap_cli/commands/facts.rb +93 -0
  13. data/lib/leap_cli/commands/inspect.rb +140 -0
  14. data/lib/leap_cli/commands/list.rb +122 -0
  15. data/lib/leap_cli/commands/new.rb +126 -0
  16. data/lib/leap_cli/commands/node.rb +272 -0
  17. data/lib/leap_cli/commands/pre.rb +99 -0
  18. data/lib/leap_cli/commands/shell.rb +67 -0
  19. data/lib/leap_cli/commands/test.rb +55 -0
  20. data/lib/leap_cli/commands/user.rb +140 -0
  21. data/lib/leap_cli/commands/util.rb +50 -0
  22. data/lib/leap_cli/commands/vagrant.rb +201 -0
  23. data/lib/leap_cli/config/macros.rb +369 -0
  24. data/lib/leap_cli/config/manager.rb +369 -0
  25. data/lib/leap_cli/config/node.rb +37 -0
  26. data/lib/leap_cli/config/object.rb +336 -0
  27. data/lib/leap_cli/config/object_list.rb +174 -0
  28. data/lib/leap_cli/config/secrets.rb +43 -0
  29. data/lib/leap_cli/config/tag.rb +18 -0
  30. data/lib/leap_cli/constants.rb +7 -0
  31. data/lib/leap_cli/leapfile.rb +97 -0
  32. data/lib/leap_cli/load_paths.rb +15 -0
  33. data/lib/leap_cli/log.rb +166 -0
  34. data/lib/leap_cli/logger.rb +216 -0
  35. data/lib/leap_cli/markdown_document_listener.rb +134 -0
  36. data/lib/leap_cli/path.rb +84 -0
  37. data/lib/leap_cli/remote/leap_plugin.rb +204 -0
  38. data/lib/leap_cli/remote/puppet_plugin.rb +66 -0
  39. data/lib/leap_cli/remote/rsync_plugin.rb +35 -0
  40. data/lib/leap_cli/remote/tasks.rb +36 -0
  41. data/lib/leap_cli/requirements.rb +19 -0
  42. data/lib/leap_cli/ssh_key.rb +130 -0
  43. data/lib/leap_cli/util/remote_command.rb +110 -0
  44. data/lib/leap_cli/util/secret.rb +54 -0
  45. data/lib/leap_cli/util/x509.rb +32 -0
  46. data/lib/leap_cli/util.rb +431 -0
  47. data/lib/leap_cli/version.rb +9 -0
  48. data/lib/leap_cli.rb +46 -0
  49. data/lib/lib_ext/capistrano_connections.rb +16 -0
  50. data/lib/lib_ext/gli.rb +52 -0
  51. data/lib/lib_ext/markdown_document_listener.rb +122 -0
  52. data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +200 -0
  53. data/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb +77 -0
  54. data/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb +97 -0
  55. data/vendor/certificate_authority/lib/certificate_authority/extensions.rb +266 -0
  56. data/vendor/certificate_authority/lib/certificate_authority/key_material.rb +148 -0
  57. data/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb +144 -0
  58. data/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb +65 -0
  59. data/vendor/certificate_authority/lib/certificate_authority/revocable.rb +14 -0
  60. data/vendor/certificate_authority/lib/certificate_authority/serial_number.rb +10 -0
  61. data/vendor/certificate_authority/lib/certificate_authority/signing_entity.rb +16 -0
  62. data/vendor/certificate_authority/lib/certificate_authority/signing_request.rb +56 -0
  63. data/vendor/certificate_authority/lib/certificate_authority.rb +21 -0
  64. data/vendor/rsync_command/lib/rsync_command/ssh_options.rb +159 -0
  65. data/vendor/rsync_command/lib/rsync_command/thread_pool.rb +36 -0
  66. data/vendor/rsync_command/lib/rsync_command/version.rb +3 -0
  67. data/vendor/rsync_command/lib/rsync_command.rb +96 -0
  68. data/vendor/rsync_command/test/rsync_test.rb +74 -0
  69. data/vendor/rsync_command/test/ssh_options_test.rb +61 -0
  70. data/vendor/vagrant_ssh_keys/vagrant.key +27 -0
  71. data/vendor/vagrant_ssh_keys/vagrant.pub +1 -0
  72. metadata +345 -0
@@ -0,0 +1,148 @@
1
+ module CertificateAuthority
2
+ module KeyMaterial
3
+ def public_key
4
+ raise "Required implementation"
5
+ end
6
+
7
+ def private_key
8
+ raise "Required implementation"
9
+ end
10
+
11
+ def is_in_hardware?
12
+ raise "Required implementation"
13
+ end
14
+
15
+ def is_in_memory?
16
+ raise "Required implementation"
17
+ end
18
+
19
+ def self.from_x509_key_pair(pair,password=nil)
20
+ if password.nil?
21
+ key = OpenSSL::PKey::RSA.new(pair)
22
+ else
23
+ key = OpenSSL::PKey::RSA.new(pair,password)
24
+ end
25
+ mem_key = MemoryKeyMaterial.new
26
+ mem_key.public_key = key.public_key
27
+ mem_key.private_key = key
28
+ mem_key
29
+ end
30
+
31
+ def self.from_x509_public_key(public_key_pem)
32
+ key = OpenSSL::PKey::RSA.new(public_key_pem)
33
+ signing_request_key = SigningRequestKeyMaterial.new
34
+ signing_request_key.public_key = key.public_key
35
+ signing_request_key
36
+ end
37
+ end
38
+
39
+ class MemoryKeyMaterial
40
+ include KeyMaterial
41
+ include ActiveModel::Validations
42
+
43
+ attr_accessor :keypair
44
+ attr_accessor :private_key
45
+ attr_accessor :public_key
46
+
47
+ def initialize
48
+ end
49
+
50
+ validates_each :private_key do |record, attr, value|
51
+ record.errors.add :private_key, "cannot be blank" if record.private_key.nil?
52
+ end
53
+ validates_each :public_key do |record, attr, value|
54
+ record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
55
+ end
56
+
57
+ def is_in_hardware?
58
+ false
59
+ end
60
+
61
+ def is_in_memory?
62
+ true
63
+ end
64
+
65
+ def generate_key(modulus_bits=2048)
66
+ self.keypair = OpenSSL::PKey::RSA.new(modulus_bits)
67
+ self.private_key = keypair
68
+ self.public_key = keypair.public_key
69
+ self.keypair
70
+ end
71
+
72
+ def private_key
73
+ @private_key
74
+ end
75
+
76
+ def public_key
77
+ @public_key
78
+ end
79
+ end
80
+
81
+ class SigningRequestKeyMaterial
82
+ include KeyMaterial
83
+ include ActiveModel::Validations
84
+
85
+ validates_each :public_key do |record, attr, value|
86
+ record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
87
+ end
88
+
89
+ attr_accessor :public_key
90
+
91
+ def initialize(request=nil)
92
+ if request.is_a? OpenSSL::X509::Request
93
+ raise "Invalid certificate signing request" unless request.verify request.public_key
94
+ self.public_key = request.public_key
95
+ end
96
+ end
97
+
98
+ def is_in_hardware?
99
+ false
100
+ end
101
+
102
+ def is_in_memory?
103
+ true
104
+ end
105
+
106
+ def private_key
107
+ nil
108
+ end
109
+
110
+ def public_key
111
+ @public_key
112
+ end
113
+ end
114
+
115
+ class SigningRequestKeyMaterial
116
+ include KeyMaterial
117
+ include ActiveModel::Validations
118
+
119
+ validates_each :public_key do |record, attr, value|
120
+ record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
121
+ end
122
+
123
+ attr_accessor :public_key
124
+
125
+ def initialize(request=nil)
126
+ if request.is_a? OpenSSL::X509::Request
127
+ raise "Invalid certificate signing request" unless request.verify request.public_key
128
+ self.public_key = request.public_key
129
+ end
130
+ end
131
+
132
+ def is_in_hardware?
133
+ false
134
+ end
135
+
136
+ def is_in_memory?
137
+ true
138
+ end
139
+
140
+ def private_key
141
+ nil
142
+ end
143
+
144
+ def public_key
145
+ @public_key
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,144 @@
1
+ module CertificateAuthority
2
+ class OCSPResponseBuilder
3
+ attr_accessor :ocsp_response
4
+ attr_accessor :verification_mechanism
5
+ attr_accessor :ocsp_request_reader
6
+ attr_accessor :parent
7
+ attr_accessor :next_update
8
+
9
+ GOOD = OpenSSL::OCSP::V_CERTSTATUS_GOOD
10
+ REVOKED = OpenSSL::OCSP::V_CERTSTATUS_REVOKED
11
+
12
+ NO_REASON=0
13
+ KEY_COMPROMISED=OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE
14
+ UNSPECIFIED=OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED
15
+
16
+ def build_response()
17
+ raise "Requires a parent for signing" if @parent.nil?
18
+ if @verification_mechanism.nil?
19
+ ## If no verification callback is provided we're marking it GOOD
20
+ @verification_mechanism = lambda {|cert_id| [GOOD,NO_REASON] }
21
+ end
22
+
23
+ @ocsp_request_reader.ocsp_request.certid.each do |cert_id|
24
+ result,reason = verification_mechanism.call(cert_id.serial)
25
+
26
+ ## cert_id, status, reason, rev_time, this update, next update, ext
27
+ ## - unit of time is seconds
28
+ ## - rev_time is currently set to "now"
29
+ @ocsp_response.add_status(cert_id,
30
+ result, reason,
31
+ 0, 0, @next_update, nil)
32
+ end
33
+
34
+ @ocsp_response.sign(OpenSSL::X509::Certificate.new(@parent.to_pem), @parent.key_material.private_key, nil, nil)
35
+ OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, @ocsp_response)
36
+ end
37
+
38
+ def self.from_request_reader(request_reader,verification_mechanism=nil)
39
+ response_builder = OCSPResponseBuilder.new
40
+ response_builder.ocsp_request_reader = request_reader
41
+
42
+ ocsp_response = OpenSSL::OCSP::BasicResponse.new
43
+ ocsp_response.copy_nonce(request_reader.ocsp_request)
44
+ response_builder.ocsp_response = ocsp_response
45
+ response_builder.next_update = 60*15 #Default of 15 minutes
46
+ response_builder
47
+ end
48
+ end
49
+
50
+ class OCSPRequestReader
51
+ attr_accessor :raw_ocsp_request
52
+ attr_accessor :ocsp_request
53
+
54
+ def serial_numbers
55
+ @ocsp_request.certid.collect do |cert_id|
56
+ cert_id.serial
57
+ end
58
+ end
59
+
60
+ def self.from_der(request_body)
61
+ reader = OCSPRequestReader.new
62
+ reader.raw_ocsp_request = request_body
63
+ reader.ocsp_request = OpenSSL::OCSP::Request.new(request_body)
64
+
65
+ reader
66
+ end
67
+ end
68
+
69
+ ## DEPRECATED
70
+ class OCSPHandler
71
+ include ActiveModel::Validations
72
+
73
+ attr_accessor :ocsp_request
74
+ attr_accessor :certificate_ids
75
+
76
+ attr_accessor :certificates
77
+ attr_accessor :parent
78
+
79
+ attr_accessor :ocsp_response_body
80
+
81
+ validate do |crl|
82
+ errors.add :parent, "A parent entity must be set" if parent.nil?
83
+ end
84
+ validate :all_certificates_available
85
+
86
+ def initialize
87
+ self.certificates = {}
88
+ end
89
+
90
+ def <<(cert)
91
+ self.certificates[cert.serial_number.number.to_s] = cert
92
+ end
93
+
94
+ def extract_certificate_serials
95
+ openssl_request = OpenSSL::OCSP::Request.new(@ocsp_request)
96
+
97
+ self.certificate_ids = openssl_request.certid.collect do |cert_id|
98
+ cert_id.serial
99
+ end
100
+
101
+ self.certificate_ids
102
+ end
103
+
104
+
105
+ def response
106
+ raise "Invalid response" unless valid?
107
+
108
+ openssl_ocsp_response = OpenSSL::OCSP::BasicResponse.new
109
+ openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
110
+ openssl_ocsp_response.copy_nonce(openssl_ocsp_request)
111
+
112
+ openssl_ocsp_request.certid.each do |cert_id|
113
+ certificate = self.certificates[cert_id.serial.to_s]
114
+
115
+ openssl_ocsp_response.add_status(cert_id,
116
+ OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0,
117
+ 0, 0, 30, nil)
118
+ end
119
+
120
+
121
+ openssl_ocsp_response.sign(OpenSSL::X509::Certificate.new(self.parent.to_pem), self.parent.key_material.private_key, nil, nil)
122
+ final_response = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, openssl_ocsp_response)
123
+ self.ocsp_response_body = final_response
124
+ self.ocsp_response_body
125
+ end
126
+
127
+ def to_der
128
+ raise "No signed OCSP response body available" if self.ocsp_response_body.nil?
129
+ self.ocsp_response_body.to_der
130
+ end
131
+
132
+ private
133
+
134
+ def all_certificates_available
135
+ openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
136
+
137
+ openssl_ocsp_request.certid.each do |cert_id|
138
+ certificate = self.certificates[cert_id.serial.to_s]
139
+ errors.add(:base, "Certificate #{cert_id.serial} has not been added yet") if certificate.nil?
140
+ end
141
+ end
142
+
143
+ end
144
+ end
@@ -0,0 +1,65 @@
1
+ module CertificateAuthority
2
+ class Pkcs11KeyMaterial
3
+ include KeyMaterial
4
+ include ActiveModel::Validations
5
+ include ActiveModel::Serialization
6
+
7
+ attr_accessor :engine
8
+ attr_accessor :token_id
9
+ attr_accessor :pkcs11_lib
10
+ attr_accessor :openssl_pkcs11_engine_lib
11
+ attr_accessor :pin
12
+
13
+ def initialize(attributes = {})
14
+ @attributes = attributes
15
+ initialize_engine
16
+ end
17
+
18
+ def is_in_hardware?
19
+ true
20
+ end
21
+
22
+ def is_in_memory?
23
+ false
24
+ end
25
+
26
+ def generate_key(modulus_bits=1024)
27
+ puts "Key generation is not currently supported in hardware"
28
+ nil
29
+ end
30
+
31
+ def private_key
32
+ initialize_engine
33
+ self.engine.load_private_key(self.token_id)
34
+ end
35
+
36
+ def public_key
37
+ initialize_engine
38
+ self.engine.load_public_key(self.token_id)
39
+ end
40
+
41
+ private
42
+
43
+ def initialize_engine
44
+ ## We're going to return early and try again later if params weren't passed in
45
+ ## at initialization. Any attempt at getting a public/private key will try
46
+ ## again.
47
+ return false if self.openssl_pkcs11_engine_lib.nil? or self.pkcs11_lib.nil?
48
+ return self.engine unless self.engine.nil?
49
+ OpenSSL::Engine.load
50
+
51
+ pkcs11 = OpenSSL::Engine.by_id("dynamic") do |e|
52
+ e.ctrl_cmd("SO_PATH",self.openssl_pkcs11_engine_lib)
53
+ e.ctrl_cmd("ID","pkcs11")
54
+ e.ctrl_cmd("LIST_ADD","1")
55
+ e.ctrl_cmd("LOAD")
56
+ e.ctrl_cmd("PIN",self.pin) unless self.pin.nil? or self.pin == ""
57
+ e.ctrl_cmd("MODULE_PATH",self.pkcs11_lib)
58
+ end
59
+
60
+ self.engine = pkcs11
61
+ pkcs11
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,14 @@
1
+ module CertificateAuthority
2
+ module Revocable
3
+ attr_accessor :revoked_at
4
+
5
+ def revoke!(time=Time.now)
6
+ @revoked_at = time
7
+ end
8
+
9
+ def revoked?
10
+ # If we have a time, then we're revoked
11
+ !@revoked_at.nil?
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module CertificateAuthority
2
+ class SerialNumber
3
+ include ActiveModel::Validations
4
+ include Revocable
5
+
6
+ attr_accessor :number
7
+
8
+ validates :number, :presence => true, :numericality => {:greater_than => 0}
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module CertificateAuthority
2
+ module SigningEntity
3
+
4
+ def self.included(mod)
5
+ mod.class_eval do
6
+ attr_accessor :signing_entity
7
+ end
8
+ end
9
+
10
+ def signing_entity=(val)
11
+ raise "invalid param" unless [true,false].include?(val)
12
+ @signing_entity = val
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,56 @@
1
+ module CertificateAuthority
2
+ class SigningRequest
3
+ attr_accessor :distinguished_name
4
+ attr_accessor :key_material
5
+ attr_accessor :raw_body
6
+ attr_accessor :openssl_csr
7
+ attr_accessor :digest
8
+
9
+ def to_cert
10
+ cert = Certificate.new
11
+ if !@distinguished_name.nil?
12
+ cert.distinguished_name = @distinguished_name
13
+ end
14
+ cert.key_material = @key_material
15
+ cert
16
+ end
17
+
18
+ def to_pem
19
+ to_x509_csr.to_pem
20
+ end
21
+
22
+ def to_x509_csr
23
+ raise "Must specify a DN/subject on csr" if @distinguished_name.nil?
24
+ raise "Invalid DN in request" unless @distinguished_name.valid?
25
+ raise "CSR must have key material" if @key_material.nil?
26
+ raise "CSR must include a public key on key material" if @key_material.public_key.nil?
27
+
28
+ opensslcsr = OpenSSL::X509::Request.new
29
+ opensslcsr.subject = @distinguished_name.to_x509_name
30
+ opensslcsr.public_key = @key_material.public_key
31
+ opensslcsr.sign @key_material.private_key, OpenSSL::Digest::Digest.new(@digest || "SHA512")
32
+ opensslcsr
33
+ end
34
+
35
+ def self.from_x509_csr(raw_csr)
36
+ csr = SigningRequest.new
37
+ openssl_csr = OpenSSL::X509::Request.new(raw_csr)
38
+ csr.distinguished_name = DistinguishedName.from_openssl openssl_csr.subject
39
+ csr.raw_body = raw_csr
40
+ csr.openssl_csr = openssl_csr
41
+ key_material = SigningRequestKeyMaterial.new
42
+ key_material.public_key = openssl_csr.public_key
43
+ csr.key_material = key_material
44
+ csr
45
+ end
46
+
47
+ def self.from_netscape_spkac(raw_spkac)
48
+ openssl_spkac = OpenSSL::Netscape::SPKI.new raw_spkac
49
+ csr = SigningRequest.new
50
+ csr.raw_body = raw_spkac
51
+ key_material = SigningRequestKeyMaterial.new
52
+ key_material.public_key = openssl_spkac.public_key
53
+ csr
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,21 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ #Exterior requirements
4
+ require 'openssl'
5
+ require 'active_model'
6
+
7
+ #Internal modules
8
+ require 'certificate_authority/signing_entity'
9
+ require 'certificate_authority/revocable'
10
+ require 'certificate_authority/distinguished_name'
11
+ require 'certificate_authority/serial_number'
12
+ require 'certificate_authority/key_material'
13
+ require 'certificate_authority/pkcs11_key_material'
14
+ require 'certificate_authority/extensions'
15
+ require 'certificate_authority/certificate'
16
+ require 'certificate_authority/certificate_revocation_list'
17
+ require 'certificate_authority/ocsp_handler'
18
+ require 'certificate_authority/signing_request'
19
+
20
+ module CertificateAuthority
21
+ end
@@ -0,0 +1,159 @@
1
+ #
2
+ # Converts capistrano-style ssh configuration (which uses Net::SSH) into a OpenSSH command line flags suitable for rsync.
3
+ #
4
+ # For a list of the options normally support by Net::SSH (and thus Capistrano), see
5
+ # http://net-ssh.github.com/net-ssh/classes/Net/SSH.html#method-c-start
6
+ #
7
+ # Also, to see how Net::SSH does the opposite of the conversion we are doing here, check out:
8
+ # https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/config.rb
9
+ #
10
+ # API mismatch:
11
+ #
12
+ # * many OpenSSH options not supported
13
+ # * some options only make sense for Net::SSH
14
+ # * compression: for Net::SSH, this option is supposed to accept true, false, or algorithm. OpenSSH accepts 'yes' or 'no'
15
+ #
16
+ class RsyncCommand
17
+ class SshOptions
18
+
19
+ def initialize(options={})
20
+ @options = parse_options(options)
21
+ end
22
+
23
+ def to_flags
24
+ if @options.empty?
25
+ nil
26
+ else
27
+ %[-e "ssh #{@options.join(' ')}"]
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def parse_options(options)
34
+ options.map do |key, value|
35
+ next if value.nil?
36
+ # Convert Net::SSH options into OpenSSH options.
37
+ case key
38
+ when :auth_methods then opt_auth_methods(value)
39
+ when :bind_address then opt('BindAddress', value)
40
+ when :compression then opt('Compression', value ? 'yes' : 'no')
41
+ when :compression_level then opt('CompressionLevel', value.to_i)
42
+ when :config then value ? "-F '#{value}'" : nil
43
+ when :encryption then opt('Ciphers', [value].flatten.join(','))
44
+ when :forward_agent then opt('ForwardAgent', value)
45
+ when :global_known_hosts_file then opt('GlobalKnownHostsFile', value)
46
+ when :hmac then opt('MACs', [value].flatten.join(','))
47
+ when :host_key then opt('HostKeyAlgorithms', [value].flatten.join(','))
48
+ when :host_key_alias then opt('HostKeyAlias', value)
49
+ when :host_name then opt('HostName', value)
50
+ when :kex then opt('KexAlgorithms', [value].flatten.join(','))
51
+ when :key_data then nil # not supported
52
+ when :keys then [value].flatten.select { |k| File.exist?(k) }.map { |k| "-i '#{k}'" }
53
+ when :keys_only then opt('IdentitiesOnly', value ? 'yes' : 'no')
54
+ when :languages then nil # not applicable
55
+ when :logger then nil # not applicable
56
+ when :paranoid then opt('StrictHostKeyChecking', value ? 'yes' : 'no')
57
+ when :passphrase then nil # not supported
58
+ when :password then nil # not supported
59
+ when :port then "-p #{value.to_i}"
60
+ when :properties then nil # not applicable
61
+ when :proxy then nil # not applicable
62
+ when :rekey_blocks_limit then nil # not supported
63
+ when :rekey_limit then opt('RekeyLimit', reverse_interpret_size(value))
64
+ when :rekey_packet_limit then nil # not supported
65
+ when :timeout then opt('ConnectTimeout', value.to_i)
66
+ when :user then "-l #{value}"
67
+ when :user_known_hosts_file then multi_opt('UserKnownHostsFile', value)
68
+ when :verbose then opt('LogLevel', interpret_log_level(value))
69
+ end
70
+ end.compact
71
+ end
72
+
73
+ private
74
+
75
+ def opt(option_name, option_value)
76
+ "-o #{option_name}='#{option_value}'"
77
+ end
78
+
79
+ def multi_opt(option_name, option_values)
80
+ [option_values].flatten.map do |value|
81
+ opt(option_name, value)
82
+ end.join(' ')
83
+ end
84
+
85
+ #
86
+ # In OpenSSH, password and pubkey default to 'yes', hostbased defaults to 'no'.
87
+ # Regardless, if :auth_method is configured, then we explicitly set the auth method.
88
+ #
89
+ def opt_auth_methods(value)
90
+ value = [value].flatten
91
+ opts = []
92
+ if value.any?
93
+ if value.include? 'password'
94
+ opts << opt('PasswordAuthentication', 'yes')
95
+ else
96
+ opts << opt('PasswordAuthentication', 'no')
97
+ end
98
+ if value.include? 'publickey'
99
+ opts << opt('PubkeyAuthentication', 'yes')
100
+ else
101
+ opts << opt('PubkeyAuthentication', 'no')
102
+ end
103
+ if value.include? 'hostbased'
104
+ opts << opt('HostbasedAuthentication', 'yes')
105
+ else
106
+ opts << opt('HostbasedAuthentication', 'no')
107
+ end
108
+ end
109
+ if opts.any?
110
+ return opts.join(' ')
111
+ else
112
+ nil
113
+ end
114
+ end
115
+
116
+ #
117
+ # Converts the given integer size in bytes into a string with 'K', 'M', 'G' suffix, as appropriate.
118
+ #
119
+ # reverse of interpret_size in https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/config.rb
120
+ #
121
+ def reverse_interpret_size(size)
122
+ size = size.to_i
123
+ if size < 1024
124
+ "#{size}"
125
+ elsif size < 1024 * 1024
126
+ "#{size/1024}K"
127
+ elsif size < 1024 * 1024 * 1024
128
+ "#{size/(1024*1024)}M"
129
+ else
130
+ "#{size/(1024*1024*1024)}G"
131
+ end
132
+ end
133
+
134
+ def interpret_log_level(level)
135
+ if level.is_a? Symbol
136
+ case level
137
+ when :debug then "DEBUG"
138
+ when :info then "INFO"
139
+ when :warn then "ERROR"
140
+ when :error then "ERROR"
141
+ when :fatal then "FATAL"
142
+ else "INFO"
143
+ end
144
+ elsif level.is_a?(Integer) && defined?(Logger)
145
+ case level
146
+ when Logger::DEBUG then "DEBUG"
147
+ when Logger::INFO then "INFO"
148
+ when Logger::WARN then "ERROR"
149
+ when Logger::ERROR then "ERROR"
150
+ when Logger::FATAL then "FATAL"
151
+ else "INFO"
152
+ end
153
+ else
154
+ "INFO"
155
+ end
156
+ end
157
+
158
+ end
159
+ end
@@ -0,0 +1,36 @@
1
+ require 'thread'
2
+
3
+ class RsyncCommand
4
+ class ThreadPool
5
+ class << self
6
+ attr_accessor :default_size
7
+ end
8
+
9
+ def initialize(size=nil)
10
+ @size = size || ThreadPool.default_size || 10
11
+ @jobs = Queue.new
12
+ @retvals = []
13
+ @pool = Array.new(@size) do |i|
14
+ Thread.new do
15
+ Thread.current[:id] = i
16
+ catch(:exit) do
17
+ loop do
18
+ job, args = @jobs.pop
19
+ @retvals << job.call(*args)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ def schedule(*args, &block)
26
+ @jobs << [block, args]
27
+ end
28
+ def shutdown
29
+ @size.times do
30
+ schedule { throw :exit }
31
+ end
32
+ @pool.map(&:join)
33
+ @retvals
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ class RsyncCommand
2
+ VERSION = "0.0.1"
3
+ end