leap_cli 1.8.1 → 1.9
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.
- checksums.yaml +4 -4
- data/bin/leap +6 -12
- data/lib/leap_cli.rb +3 -23
- data/lib/leap_cli/bootstrap.rb +36 -12
- data/lib/leap_cli/commands/common.rb +88 -46
- data/lib/leap_cli/commands/new.rb +24 -17
- data/lib/leap_cli/commands/pre.rb +3 -1
- data/lib/leap_cli/core_ext/hash.rb +19 -0
- data/lib/leap_cli/leapfile.rb +47 -32
- data/lib/leap_cli/log.rb +196 -88
- data/lib/leap_cli/path.rb +5 -5
- data/lib/leap_cli/util.rb +28 -18
- data/lib/leap_cli/version.rb +8 -3
- data/vendor/acme-client/lib/acme-client.rb +1 -0
- data/vendor/acme-client/lib/acme/client.rb +122 -0
- data/vendor/acme-client/lib/acme/client/certificate.rb +30 -0
- data/vendor/acme-client/lib/acme/client/certificate_request.rb +111 -0
- data/vendor/acme-client/lib/acme/client/crypto.rb +98 -0
- data/vendor/acme-client/lib/acme/client/error.rb +16 -0
- data/vendor/acme-client/lib/acme/client/faraday_middleware.rb +123 -0
- data/vendor/acme-client/lib/acme/client/resources.rb +5 -0
- data/vendor/acme-client/lib/acme/client/resources/authorization.rb +44 -0
- data/vendor/acme-client/lib/acme/client/resources/challenges.rb +6 -0
- data/vendor/acme-client/lib/acme/client/resources/challenges/base.rb +43 -0
- data/vendor/acme-client/lib/acme/client/resources/challenges/dns01.rb +19 -0
- data/vendor/acme-client/lib/acme/client/resources/challenges/http01.rb +18 -0
- data/vendor/acme-client/lib/acme/client/resources/challenges/tls_sni01.rb +24 -0
- data/vendor/acme-client/lib/acme/client/resources/registration.rb +37 -0
- data/vendor/acme-client/lib/acme/client/self_sign_certificate.rb +60 -0
- data/vendor/acme-client/lib/acme/client/version.rb +7 -0
- data/vendor/base32/lib/base32.rb +67 -0
- data/vendor/certificate_authority/lib/certificate_authority.rb +2 -1
- data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +4 -4
- data/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb +7 -5
- data/vendor/certificate_authority/lib/certificate_authority/core_extensions.rb +46 -0
- data/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb +6 -2
- data/vendor/certificate_authority/lib/certificate_authority/extensions.rb +10 -3
- data/vendor/certificate_authority/lib/certificate_authority/key_material.rb +11 -9
- data/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb +3 -3
- data/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb +0 -2
- data/vendor/certificate_authority/lib/certificate_authority/serial_number.rb +8 -2
- data/vendor/certificate_authority/lib/certificate_authority/validations.rb +31 -0
- data/vendor/rsync_command/lib/rsync_command.rb +49 -12
- metadata +50 -91
- data/lib/leap/platform.rb +0 -90
- data/lib/leap_cli/config/environment.rb +0 -180
- data/lib/leap_cli/config/filter.rb +0 -178
- data/lib/leap_cli/config/manager.rb +0 -419
- data/lib/leap_cli/config/node.rb +0 -77
- data/lib/leap_cli/config/object.rb +0 -428
- data/lib/leap_cli/config/object_list.rb +0 -209
- data/lib/leap_cli/config/provider.rb +0 -22
- data/lib/leap_cli/config/secrets.rb +0 -87
- data/lib/leap_cli/config/sources.rb +0 -11
- data/lib/leap_cli/config/tag.rb +0 -25
- data/lib/leap_cli/lib_ext/capistrano_connections.rb +0 -16
- data/lib/leap_cli/logger.rb +0 -237
- data/lib/leap_cli/remote/leap_plugin.rb +0 -192
- data/lib/leap_cli/remote/puppet_plugin.rb +0 -26
- data/lib/leap_cli/remote/rsync_plugin.rb +0 -35
- data/lib/leap_cli/remote/tasks.rb +0 -51
- data/lib/leap_cli/ssh_key.rb +0 -195
- data/lib/leap_cli/util/remote_command.rb +0 -158
- data/lib/leap_cli/util/secret.rb +0 -55
- data/lib/leap_cli/util/x509.rb +0 -33
@@ -0,0 +1,60 @@
|
|
1
|
+
class Acme::Client::SelfSignCertificate
|
2
|
+
attr_reader :private_key, :subject_alt_names, :not_before, :not_after
|
3
|
+
|
4
|
+
extend Forwardable
|
5
|
+
def_delegators :certificate, :to_pem, :to_der
|
6
|
+
|
7
|
+
def initialize(subject_alt_names:, not_before: default_not_before, not_after: default_not_after, private_key: generate_private_key)
|
8
|
+
@private_key = private_key
|
9
|
+
@subject_alt_names = subject_alt_names
|
10
|
+
@not_before = not_before
|
11
|
+
@not_after = not_after
|
12
|
+
end
|
13
|
+
|
14
|
+
def certificate
|
15
|
+
@certificate ||= begin
|
16
|
+
certificate = generate_certificate
|
17
|
+
|
18
|
+
extension_factory = generate_extension_factory(certificate)
|
19
|
+
subject_alt_name_entry = subject_alt_names.map { |d| "DNS: #{d}" }.join(',')
|
20
|
+
subject_alt_name_extension = extension_factory.create_extension('subjectAltName', subject_alt_name_entry)
|
21
|
+
certificate.add_extension(subject_alt_name_extension)
|
22
|
+
|
23
|
+
certificate.sign(private_key, digest)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def generate_private_key
|
30
|
+
OpenSSL::PKey::RSA.new(2048)
|
31
|
+
end
|
32
|
+
|
33
|
+
def default_not_before
|
34
|
+
Time.now - 3600
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_not_after
|
38
|
+
Time.now + 30 * 24 * 3600
|
39
|
+
end
|
40
|
+
|
41
|
+
def digest
|
42
|
+
OpenSSL::Digest::SHA256.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate_certificate
|
46
|
+
certificate = OpenSSL::X509::Certificate.new
|
47
|
+
certificate.not_before = not_before
|
48
|
+
certificate.not_after = not_after
|
49
|
+
certificate.public_key = private_key.public_key
|
50
|
+
certificate.version = 2
|
51
|
+
certificate
|
52
|
+
end
|
53
|
+
|
54
|
+
def generate_extension_factory(certificate)
|
55
|
+
extension_factory = OpenSSL::X509::ExtensionFactory.new
|
56
|
+
extension_factory.subject_certificate = certificate
|
57
|
+
extension_factory.issuer_certificate = certificate
|
58
|
+
extension_factory
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
# Module for encoding and decoding in Base32 per RFC 3548
|
4
|
+
module Base32
|
5
|
+
TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.freeze
|
6
|
+
|
7
|
+
class Chunk
|
8
|
+
def initialize(bytes)
|
9
|
+
@bytes = bytes
|
10
|
+
end
|
11
|
+
|
12
|
+
def decode
|
13
|
+
bytes = @bytes.take_while {|c| c != 61} # strip padding
|
14
|
+
n = (bytes.length * 5.0 / 8.0).floor
|
15
|
+
p = bytes.length < 8 ? 5 - (n * 8) % 5 : 0
|
16
|
+
c = bytes.inject(0) {|m,o| (m << 5) + Base32.table.index(o.chr)} >> p
|
17
|
+
(0..n-1).to_a.reverse.collect {|i| ((c >> i * 8) & 0xff).chr}
|
18
|
+
end
|
19
|
+
|
20
|
+
def encode
|
21
|
+
n = (@bytes.length * 8.0 / 5.0).ceil
|
22
|
+
p = n < 8 ? 5 - (@bytes.length * 8) % 5 : 0
|
23
|
+
c = @bytes.inject(0) {|m,o| (m << 8) + o} << p
|
24
|
+
[(0..n-1).to_a.reverse.collect {|i| Base32.table[(c >> i * 5) & 0x1f].chr},
|
25
|
+
("=" * (8-n))]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.chunks(str, size)
|
30
|
+
result = []
|
31
|
+
bytes = str.bytes
|
32
|
+
while bytes.any? do
|
33
|
+
result << Chunk.new(bytes.take(size))
|
34
|
+
bytes = bytes.drop(size)
|
35
|
+
end
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.encode(str)
|
40
|
+
chunks(str, 5).collect(&:encode).flatten.join
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.decode(str)
|
44
|
+
chunks(str, 8).collect(&:decode).flatten.join
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.random_base32(length=16, padding=true)
|
48
|
+
random = ''
|
49
|
+
OpenSSL::Random.random_bytes(length).each_byte do |b|
|
50
|
+
random << self.table[b % 32]
|
51
|
+
end
|
52
|
+
padding ? random.ljust((length / 8.0).ceil * 8, '=') : random
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.table=(table)
|
56
|
+
raise ArgumentError, "Table must have 32 unique characters" unless self.table_valid?(table)
|
57
|
+
@table = table
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.table
|
61
|
+
@table || TABLE
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.table_valid?(table)
|
65
|
+
table.bytes.to_a.size == 32 && table.bytes.to_a.uniq.size == 32
|
66
|
+
end
|
67
|
+
end
|
@@ -2,11 +2,12 @@ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) ||
|
|
2
2
|
|
3
3
|
#Exterior requirements
|
4
4
|
require 'openssl'
|
5
|
-
require 'active_model'
|
6
5
|
|
7
6
|
#Internal modules
|
7
|
+
require 'certificate_authority/core_extensions'
|
8
8
|
require 'certificate_authority/signing_entity'
|
9
9
|
require 'certificate_authority/revocable'
|
10
|
+
require 'certificate_authority/validations'
|
10
11
|
require 'certificate_authority/distinguished_name'
|
11
12
|
require 'certificate_authority/serial_number'
|
12
13
|
require 'certificate_authority/key_material'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module CertificateAuthority
|
2
2
|
class Certificate
|
3
|
-
include
|
3
|
+
include Validations
|
4
4
|
include Revocable
|
5
5
|
|
6
6
|
attr_accessor :distinguished_name
|
@@ -15,7 +15,7 @@ module CertificateAuthority
|
|
15
15
|
|
16
16
|
attr_accessor :parent
|
17
17
|
|
18
|
-
validate
|
18
|
+
def validate
|
19
19
|
errors.add :base, "Distinguished name must be valid" unless distinguished_name.valid?
|
20
20
|
errors.add :base, "Key material must be valid" unless key_material.valid?
|
21
21
|
errors.add :base, "Serial number must be valid" unless serial_number.valid?
|
@@ -32,8 +32,8 @@ module CertificateAuthority
|
|
32
32
|
self.distinguished_name = DistinguishedName.new
|
33
33
|
self.serial_number = SerialNumber.new
|
34
34
|
self.key_material = MemoryKeyMaterial.new
|
35
|
-
self.not_before =
|
36
|
-
self.not_after =
|
35
|
+
self.not_before = Date.today.utc
|
36
|
+
self.not_after = Date.today.advance(:years => 1).utc
|
37
37
|
self.parent = self
|
38
38
|
self.extensions = load_extensions()
|
39
39
|
|
@@ -1,20 +1,22 @@
|
|
1
1
|
module CertificateAuthority
|
2
2
|
class CertificateRevocationList
|
3
|
-
include
|
3
|
+
include Validations
|
4
4
|
|
5
5
|
attr_accessor :certificates
|
6
6
|
attr_accessor :parent
|
7
7
|
attr_accessor :crl_body
|
8
8
|
attr_accessor :next_update
|
9
|
+
attr_accessor :last_update_skew_seconds
|
9
10
|
|
10
|
-
validate
|
11
|
-
errors.add :next_update, "Next update must be a positive value" if
|
12
|
-
errors.add :parent, "A parent entity must be set" if
|
11
|
+
def validate
|
12
|
+
errors.add :next_update, "Next update must be a positive value" if self.next_update < 0
|
13
|
+
errors.add :parent, "A parent entity must be set" if self.parent.nil?
|
13
14
|
end
|
14
15
|
|
15
16
|
def initialize
|
16
17
|
self.certificates = []
|
17
18
|
self.next_update = 60 * 60 * 4 # 4 hour default
|
19
|
+
self.last_update_skew_seconds = 0
|
18
20
|
end
|
19
21
|
|
20
22
|
def <<(revocable)
|
@@ -54,7 +56,7 @@ module CertificateAuthority
|
|
54
56
|
end
|
55
57
|
|
56
58
|
crl.version = 1
|
57
|
-
crl.last_update = Time.now
|
59
|
+
crl.last_update = Time.now - self.last_update_skew_seconds
|
58
60
|
crl.next_update = Time.now + self.next_update
|
59
61
|
|
60
62
|
signing_cert = OpenSSL::X509::Certificate.new(self.parent.to_pem)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# ActiveSupport has these modifications. Now that we don't use ActiveSupport,
|
3
|
+
# these are added here as a kindness.
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'date'
|
7
|
+
|
8
|
+
unless nil.respond_to?(:blank?)
|
9
|
+
class NilClass
|
10
|
+
def blank?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
unless String.respond_to?(:blank?)
|
17
|
+
class String
|
18
|
+
def blank?
|
19
|
+
self.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Date
|
25
|
+
|
26
|
+
def today
|
27
|
+
t = Time.now.utc
|
28
|
+
Date.new(t.year, t.month, t.day)
|
29
|
+
end
|
30
|
+
|
31
|
+
def utc
|
32
|
+
self.to_datetime.to_time.utc
|
33
|
+
end
|
34
|
+
|
35
|
+
unless Date.respond_to?(:advance)
|
36
|
+
def advance(options)
|
37
|
+
options = options.dup
|
38
|
+
d = self
|
39
|
+
d = d >> options.delete(:years) * 12 if options[:years]
|
40
|
+
d = d >> options.delete(:months) if options[:months]
|
41
|
+
d = d + options.delete(:weeks) * 7 if options[:weeks]
|
42
|
+
d = d + options.delete(:days) if options[:days]
|
43
|
+
d
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,8 +1,12 @@
|
|
1
1
|
module CertificateAuthority
|
2
2
|
class DistinguishedName
|
3
|
-
include
|
3
|
+
include Validations
|
4
4
|
|
5
|
-
|
5
|
+
def validate
|
6
|
+
if self.common_name.nil? || self.common_name.empty?
|
7
|
+
errors.add :common_name, 'cannot be blank'
|
8
|
+
end
|
9
|
+
end
|
6
10
|
|
7
11
|
attr_accessor :common_name
|
8
12
|
alias :cn :common_name
|
@@ -31,13 +31,20 @@ module CertificateAuthority
|
|
31
31
|
OPENSSL_IDENTIFIER = "basicConstraints"
|
32
32
|
|
33
33
|
include ExtensionAPI
|
34
|
-
include
|
34
|
+
include Validations
|
35
35
|
|
36
36
|
attr_accessor :critical
|
37
37
|
attr_accessor :ca
|
38
38
|
attr_accessor :path_len
|
39
|
-
|
40
|
-
|
39
|
+
|
40
|
+
def validate
|
41
|
+
unless [true, false].include? self.critical
|
42
|
+
errors.add :critical, 'must be true or false'
|
43
|
+
end
|
44
|
+
unless [true, false].include? self.ca
|
45
|
+
errors.add :ca, 'must be true or false'
|
46
|
+
end
|
47
|
+
end
|
41
48
|
|
42
49
|
def initialize
|
43
50
|
@critical = false
|
@@ -38,7 +38,7 @@ module CertificateAuthority
|
|
38
38
|
|
39
39
|
class MemoryKeyMaterial
|
40
40
|
include KeyMaterial
|
41
|
-
include
|
41
|
+
include Validations
|
42
42
|
|
43
43
|
attr_accessor :keypair
|
44
44
|
attr_accessor :private_key
|
@@ -47,11 +47,13 @@ module CertificateAuthority
|
|
47
47
|
def initialize
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
def validate
|
51
|
+
if private_key.nil?
|
52
|
+
errors.add :private_key, "cannot be blank"
|
53
|
+
end
|
54
|
+
if public_key.nil?
|
55
|
+
errors.add :public_key, "cannot be blank"
|
56
|
+
end
|
55
57
|
end
|
56
58
|
|
57
59
|
def is_in_hardware?
|
@@ -80,10 +82,10 @@ module CertificateAuthority
|
|
80
82
|
|
81
83
|
class SigningRequestKeyMaterial
|
82
84
|
include KeyMaterial
|
83
|
-
include
|
85
|
+
include Validations
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
+
def validate
|
88
|
+
errors.add :public_key, "cannot be blank" if public_key.nil?
|
87
89
|
end
|
88
90
|
|
89
91
|
attr_accessor :public_key
|
@@ -68,7 +68,7 @@ module CertificateAuthority
|
|
68
68
|
|
69
69
|
## DEPRECATED
|
70
70
|
class OCSPHandler
|
71
|
-
include
|
71
|
+
include Validations
|
72
72
|
|
73
73
|
attr_accessor :ocsp_request
|
74
74
|
attr_accessor :certificate_ids
|
@@ -78,10 +78,10 @@ module CertificateAuthority
|
|
78
78
|
|
79
79
|
attr_accessor :ocsp_response_body
|
80
80
|
|
81
|
-
validate
|
81
|
+
def validate
|
82
82
|
errors.add :parent, "A parent entity must be set" if parent.nil?
|
83
|
+
all_certificates_available
|
83
84
|
end
|
84
|
-
validate :all_certificates_available
|
85
85
|
|
86
86
|
def initialize
|
87
87
|
self.certificates = {}
|
@@ -2,12 +2,18 @@ require 'securerandom'
|
|
2
2
|
|
3
3
|
module CertificateAuthority
|
4
4
|
class SerialNumber
|
5
|
-
include
|
5
|
+
include Validations
|
6
6
|
include Revocable
|
7
7
|
|
8
8
|
attr_accessor :number
|
9
9
|
|
10
|
-
|
10
|
+
def validate
|
11
|
+
if self.number.nil?
|
12
|
+
errors.add :number, "must not be empty"
|
13
|
+
elsif self.number.to_i <= 0
|
14
|
+
errors.add :number, "must be greater than zero"
|
15
|
+
end
|
16
|
+
end
|
11
17
|
|
12
18
|
def initialize
|
13
19
|
self.number = SecureRandom.random_number(2**128-1)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# This is a super simple replacement for ActiveSupport::Validations
|
3
|
+
#
|
4
|
+
|
5
|
+
module CertificateAuthority
|
6
|
+
class Errors < Array
|
7
|
+
def add(symbol, msg)
|
8
|
+
self.push([symbol, msg])
|
9
|
+
end
|
10
|
+
def full_messages
|
11
|
+
self.map {|i| i[0].to_s + ": " + i[1]}.join("\n")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Validations
|
16
|
+
def valid?
|
17
|
+
@errors = Errors.new
|
18
|
+
validate
|
19
|
+
errors.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
# must be overridden
|
23
|
+
def validate
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
def errors
|
28
|
+
@errors ||= Errors.new
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -4,6 +4,44 @@ require "rsync_command/thread_pool"
|
|
4
4
|
|
5
5
|
require 'monitor'
|
6
6
|
|
7
|
+
class RsyncRunner
|
8
|
+
attr_accessor :logger
|
9
|
+
attr_accessor :source, :dest, :flags, :includes, :excludes
|
10
|
+
attr_accessor :user, :host
|
11
|
+
attr_accessor :chdir, :ssh
|
12
|
+
def initialize(rsync_command)
|
13
|
+
@logger = nil
|
14
|
+
@source = ""
|
15
|
+
@dest = ""
|
16
|
+
@flags = ""
|
17
|
+
@includes = []
|
18
|
+
@excludes = []
|
19
|
+
@rsync_command = rsync_command
|
20
|
+
end
|
21
|
+
def log(*args)
|
22
|
+
@logger.log(*args)
|
23
|
+
end
|
24
|
+
def valid?
|
25
|
+
!@source.empty? || !@dest.empty?
|
26
|
+
end
|
27
|
+
def to_hash
|
28
|
+
fields = [:flags, :includes, :excludes, :logger, :ssh, :chdir]
|
29
|
+
fields.inject({}){|hsh, i|
|
30
|
+
hsh[i] = self.send(i); hsh
|
31
|
+
}
|
32
|
+
end
|
33
|
+
def exec
|
34
|
+
return unless valid?
|
35
|
+
dest = {
|
36
|
+
:user => self.user,
|
37
|
+
:host => self.host,
|
38
|
+
:path => self.dest
|
39
|
+
}
|
40
|
+
src = self.source
|
41
|
+
@rsync_command.exec_rsync(src, dest, self.to_hash)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
7
45
|
class RsyncCommand
|
8
46
|
attr_accessor :failures, :logger
|
9
47
|
|
@@ -21,15 +59,23 @@ class RsyncCommand
|
|
21
59
|
def asynchronously(array, &block)
|
22
60
|
pool = ThreadPool.new
|
23
61
|
array.each do |item|
|
24
|
-
pool.schedule(item, &block)
|
62
|
+
pool.schedule(RsyncRunner.new(self), item, &block)
|
25
63
|
end
|
26
64
|
pool.shutdown
|
27
65
|
end
|
28
66
|
|
67
|
+
#
|
68
|
+
# returns true if last exec returned a failure
|
69
|
+
#
|
70
|
+
def failed?
|
71
|
+
@failures && @failures.any?
|
72
|
+
end
|
73
|
+
|
29
74
|
#
|
30
75
|
# runs rsync, recording failures
|
31
76
|
#
|
32
|
-
def
|
77
|
+
def exec_rsync(src, dest, options={})
|
78
|
+
logger = options[:logger] || @logger
|
33
79
|
@failures.synchronize do
|
34
80
|
@failures.clear
|
35
81
|
end
|
@@ -37,7 +83,7 @@ class RsyncCommand
|
|
37
83
|
if options[:chdir]
|
38
84
|
rsync_cmd = "cd '#{options[:chdir]}'; #{rsync_cmd}"
|
39
85
|
end
|
40
|
-
|
86
|
+
logger.debug rsync_cmd if logger
|
41
87
|
ok = system(rsync_cmd)
|
42
88
|
unless ok
|
43
89
|
@failures.synchronize do
|
@@ -46,13 +92,6 @@ class RsyncCommand
|
|
46
92
|
end
|
47
93
|
end
|
48
94
|
|
49
|
-
#
|
50
|
-
# returns true if last exec returned a failure
|
51
|
-
#
|
52
|
-
def failed?
|
53
|
-
@failures && @failures.any?
|
54
|
-
end
|
55
|
-
|
56
95
|
#
|
57
96
|
# build rsync command
|
58
97
|
#
|
@@ -70,8 +109,6 @@ class RsyncCommand
|
|
70
109
|
"rsync #{flags.compact.join(' ')} #{src} #{dest}"
|
71
110
|
end
|
72
111
|
|
73
|
-
private
|
74
|
-
|
75
112
|
#
|
76
113
|
# Creates an rsync location if the +address+ is a hash with keys :user, :host, and :path
|
77
114
|
# (each component is optional). If +address+ is a string, we just pass it through.
|