leap_cli 1.6.2 → 1.7.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.
- checksums.yaml +7 -0
- data/bin/leap +20 -6
- data/lib/leap/platform.rb +10 -12
- data/lib/leap_cli.rb +5 -3
- data/lib/leap_cli/commands/ca.rb +46 -35
- data/lib/leap_cli/commands/compile.rb +6 -2
- data/lib/leap_cli/commands/db.rb +42 -6
- data/lib/leap_cli/commands/deploy.rb +117 -35
- data/lib/leap_cli/commands/pre.rb +49 -34
- data/lib/leap_cli/commands/ssh.rb +84 -16
- data/lib/leap_cli/commands/user.rb +1 -1
- data/lib/leap_cli/commands/vagrant.rb +38 -51
- data/lib/leap_cli/config/manager.rb +6 -3
- data/lib/leap_cli/config/object.rb +41 -2
- data/lib/leap_cli/config/object_list.rb +1 -1
- data/lib/leap_cli/config/secrets.rb +18 -7
- data/lib/leap_cli/config/sources.rb +11 -0
- data/lib/leap_cli/core_ext/deep_dup.rb +53 -0
- data/lib/leap_cli/core_ext/time.rb +86 -0
- data/lib/leap_cli/leapfile.rb +21 -10
- data/lib/leap_cli/logger.rb +5 -5
- data/lib/leap_cli/remote/leap_plugin.rb +11 -0
- data/lib/leap_cli/remote/puppet_plugin.rb +1 -1
- data/lib/leap_cli/remote/tasks.rb +3 -2
- data/lib/leap_cli/util.rb +31 -23
- data/lib/leap_cli/util/secret.rb +1 -1
- data/lib/leap_cli/version.rb +2 -2
- data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +2 -2
- data/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb +2 -2
- data/vendor/certificate_authority/lib/certificate_authority/signing_request.rb +1 -1
- metadata +105 -168
@@ -13,18 +13,29 @@ module LeapCli; module Config
|
|
13
13
|
@discovered_keys = {}
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
# we can't use fetch() or get(), since those already have special meanings
|
17
|
+
def retrieve(key, environment)
|
18
|
+
self.fetch(environment, {})[key.to_s]
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(*args, &block)
|
22
|
+
if block_given?
|
23
|
+
set_with_block(*args, &block)
|
24
|
+
else
|
25
|
+
set_without_block(*args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_without_block(key, value, environment)
|
30
|
+
set_with_block(key, environment) {value}
|
31
|
+
end
|
20
32
|
|
21
|
-
def
|
22
|
-
environment ||= 'default'
|
33
|
+
def set_with_block(key, environment, &block)
|
23
34
|
key = key.to_s
|
24
35
|
@discovered_keys[environment] ||= {}
|
25
36
|
@discovered_keys[environment][key] = true
|
26
37
|
self[environment] ||= {}
|
27
|
-
self[environment][key] ||=
|
38
|
+
self[environment][key] ||= yield
|
28
39
|
end
|
29
40
|
|
30
41
|
#
|
@@ -0,0 +1,53 @@
|
|
1
|
+
unless Hash.method_defined?(:deep_dup)
|
2
|
+
|
3
|
+
class Array
|
4
|
+
def deep_dup
|
5
|
+
map { |it| it.deep_dup }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Hash
|
10
|
+
def deep_dup
|
11
|
+
each_with_object(dup) do |(key, value), hash|
|
12
|
+
hash[key.deep_dup] = value.deep_dup
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class String
|
18
|
+
def deep_dup
|
19
|
+
self.dup
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Integer
|
24
|
+
def deep_dup
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Float
|
30
|
+
def deep_dup
|
31
|
+
self
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class TrueClass
|
36
|
+
def deep_dup
|
37
|
+
self
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class FalseClass
|
42
|
+
def deep_dup
|
43
|
+
self
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class NilClass
|
48
|
+
def deep_dup
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#
|
2
|
+
# The following methods are copied from ActiveSupport's Time extension:
|
3
|
+
# activesupport/lib/active_support/core_ext/time/calculations.rb
|
4
|
+
#
|
5
|
+
|
6
|
+
class Time
|
7
|
+
|
8
|
+
#
|
9
|
+
# Uses Date to provide precise Time calculations for years, months, and days
|
10
|
+
# according to the proleptic Gregorian calendar. The options parameter takes
|
11
|
+
# a hash with any of these keys: :years, :months, :weeks, :days, :hours,
|
12
|
+
# :minutes, :seconds.
|
13
|
+
#
|
14
|
+
def advance(options)
|
15
|
+
unless options[:weeks].nil?
|
16
|
+
options[:weeks], partial_weeks = options[:weeks].divmod(1)
|
17
|
+
options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
|
18
|
+
end
|
19
|
+
|
20
|
+
unless options[:days].nil?
|
21
|
+
options[:days], partial_days = options[:days].divmod(1)
|
22
|
+
options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
|
23
|
+
end
|
24
|
+
|
25
|
+
d = to_date.advance(options)
|
26
|
+
d = d.gregorian if d.julian?
|
27
|
+
time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
|
28
|
+
seconds_to_advance = options.fetch(:seconds, 0) +
|
29
|
+
options.fetch(:minutes, 0) * 60 +
|
30
|
+
options.fetch(:hours, 0) * 3600
|
31
|
+
|
32
|
+
if seconds_to_advance.zero?
|
33
|
+
time_advanced_by_date
|
34
|
+
else
|
35
|
+
time_advanced_by_date.since(seconds_to_advance)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def since(seconds)
|
40
|
+
self + seconds
|
41
|
+
rescue
|
42
|
+
to_datetime.since(seconds)
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Returns a new Time where one or more of the elements have been changed
|
47
|
+
# according to the options parameter. The time options (:hour, :min, :sec,
|
48
|
+
# :usec) reset cascadingly, so if only the hour is passed, then minute, sec,
|
49
|
+
# and usec is set to 0. If the hour and minute is passed, then sec and usec
|
50
|
+
# is set to 0. The options parameter takes a hash with any of these keys:
|
51
|
+
# :year, :month, :day, :hour, :min, :sec, :usec.
|
52
|
+
#
|
53
|
+
def change(options)
|
54
|
+
new_year = options.fetch(:year, year)
|
55
|
+
new_month = options.fetch(:month, month)
|
56
|
+
new_day = options.fetch(:day, day)
|
57
|
+
new_hour = options.fetch(:hour, hour)
|
58
|
+
new_min = options.fetch(:min, options[:hour] ? 0 : min)
|
59
|
+
new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
|
60
|
+
new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
|
61
|
+
|
62
|
+
if utc?
|
63
|
+
::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
|
64
|
+
elsif zone
|
65
|
+
::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
|
66
|
+
else
|
67
|
+
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
class Date
|
74
|
+
|
75
|
+
# activesupport/lib/active_support/core_ext/date/calculations.rb
|
76
|
+
def advance(options)
|
77
|
+
options = options.dup
|
78
|
+
d = self
|
79
|
+
d = d >> options.delete(:years) * 12 if options[:years]
|
80
|
+
d = d >> options.delete(:months) if options[:months]
|
81
|
+
d = d + options.delete(:weeks) * 7 if options[:weeks]
|
82
|
+
d = d + options.delete(:days) if options[:days]
|
83
|
+
d
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/lib/leap_cli/leapfile.rb
CHANGED
@@ -47,6 +47,12 @@ module LeapCli
|
|
47
47
|
# set up paths
|
48
48
|
#
|
49
49
|
@provider_directory_path = directory
|
50
|
+
begin
|
51
|
+
# load leaprc first, so that we can potentially access which environment is pinned in Leapfile
|
52
|
+
# but also load leaprc last, so that it can override what is set in Leapfile.
|
53
|
+
read_settings(leaprc_path)
|
54
|
+
rescue StandardError
|
55
|
+
end
|
50
56
|
read_settings(directory + '/Leapfile')
|
51
57
|
read_settings(leaprc_path)
|
52
58
|
@platform_directory_path = File.expand_path(@platform_directory_path || '../leap_platform', @provider_directory_path)
|
@@ -54,25 +60,26 @@ module LeapCli
|
|
54
60
|
#
|
55
61
|
# load the platform
|
56
62
|
#
|
57
|
-
|
58
|
-
|
59
|
-
Util.bail! "
|
60
|
-
"is not compatible with the platform #{@platform_directory_path} (v#{Leap::Platform.version}). " +
|
61
|
-
"You need leap command #{Leap::Platform.compatible_cli.first} to #{Leap::Platform.compatible_cli.last}."
|
63
|
+
platform_file = "#{@platform_directory_path}/platform.rb"
|
64
|
+
unless File.exists?(platform_file)
|
65
|
+
Util.bail! "ERROR: The file `#{platform_file}` does not exist. Please check the value of `@platform_directory_path` in `Leapfile` or `~/.leaprc`."
|
62
66
|
end
|
63
|
-
|
67
|
+
require "#{@platform_directory_path}/platform.rb"
|
68
|
+
if !Leap::Platform.compatible_with_cli?(LeapCli::VERSION) ||
|
69
|
+
!Leap::Platform.version_in_range?(LeapCli::COMPATIBLE_PLATFORM_VERSION)
|
64
70
|
Util.bail! "This leap command (v#{LeapCli::VERSION}) " +
|
65
|
-
"is not compatible with the platform #{@platform_directory_path} (v#{Leap::Platform.version})
|
66
|
-
"You need
|
71
|
+
"is not compatible with the platform #{@platform_directory_path} (v#{Leap::Platform.version}).\n " +
|
72
|
+
"You need either leap command #{Leap::Platform.compatible_cli.first} to #{Leap::Platform.compatible_cli.last} or " +
|
73
|
+
"platform version #{LeapCli::COMPATIBLE_PLATFORM_VERSION.first} to #{LeapCli::COMPATIBLE_PLATFORM_VERSION.last}"
|
67
74
|
end
|
68
|
-
|
69
75
|
unless @allow_production_deploy.nil?
|
70
76
|
Util::log 0, :warning, "in Leapfile: @allow_production_deploy is no longer supported."
|
71
77
|
end
|
72
78
|
unless @platform_branch.nil?
|
73
79
|
Util::log 0, :warning, "in Leapfile: @platform_branch is no longer supported."
|
74
80
|
end
|
75
|
-
|
81
|
+
@valid = true
|
82
|
+
return @valid
|
76
83
|
end
|
77
84
|
end
|
78
85
|
|
@@ -84,6 +91,10 @@ module LeapCli
|
|
84
91
|
edit_leaprc(property)
|
85
92
|
end
|
86
93
|
|
94
|
+
def valid?
|
95
|
+
!!@valid
|
96
|
+
end
|
97
|
+
|
87
98
|
private
|
88
99
|
|
89
100
|
#
|
data/lib/leap_cli/logger.rb
CHANGED
@@ -113,8 +113,8 @@ module LeapCli
|
|
113
113
|
{ :match => /sh: .+: command not found/, :color => :magenta, :match_level => 1, :priority => -30 },
|
114
114
|
|
115
115
|
# IMPORTANT
|
116
|
-
{ :match => /^err ::/, :color => :red, :match_level => 0, :priority => -10 },
|
117
|
-
{ :match => /^ERROR:/, :color => :red,
|
116
|
+
{ :match => /^err ::/, :color => :red, :match_level => 0, :priority => -10, :exit => 1},
|
117
|
+
{ :match => /^ERROR:/, :color => :red, :priority => -10, :exit => 1},
|
118
118
|
{ :match => /.*/, :color => :blue, :match_level => 0, :priority => -20 },
|
119
119
|
|
120
120
|
# CLEANUP
|
@@ -136,8 +136,8 @@ module LeapCli
|
|
136
136
|
{ :match => /^warning:/, :level => 0, :color => :yellow, :priority => -20},
|
137
137
|
{ :match => /^Duplicate declaration:/, :level => 0, :color => :red, :priority => -20},
|
138
138
|
{ :match => /Finished catalog run/, :level => 0, :color => :green, :priority => -10},
|
139
|
-
{ :match => /^
|
140
|
-
{ :match => /^
|
139
|
+
{ :match => /^APPLY COMPLETE \(changes made\)/, :level => 0, :color => :green, :priority => -10},
|
140
|
+
{ :match => /^APPLY COMPLETE \(no changes\)/, :level => 0, :color => :green, :priority => -10},
|
141
141
|
|
142
142
|
# PUPPET FATAL ERRORS
|
143
143
|
{ :match => /^err:/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
@@ -146,7 +146,7 @@ module LeapCli
|
|
146
146
|
{ :match => /^Syntax error/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
147
147
|
{ :match => /^Cannot reassign variable/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
148
148
|
{ :match => /^Could not find template/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
149
|
-
{ :match => /^
|
149
|
+
{ :match => /^APPLY COMPLETE.*fail/, :level => 0, :color => :red, :priority => -1, :exit => 1},
|
150
150
|
|
151
151
|
# TESTS
|
152
152
|
{ :match => /^PASS: /, :color => :green, :priority => -20},
|
@@ -6,6 +6,10 @@
|
|
6
6
|
module LeapCli; module Remote; module LeapPlugin
|
7
7
|
|
8
8
|
def required_packages
|
9
|
+
"puppet rsync lsb-release locales"
|
10
|
+
end
|
11
|
+
|
12
|
+
def required_wheezy_packages
|
9
13
|
"puppet ruby-hiera-puppet rsync lsb-release locales"
|
10
14
|
end
|
11
15
|
|
@@ -60,6 +64,13 @@ module LeapCli; module Remote; module LeapPlugin
|
|
60
64
|
run "touch #{Leap::Platform.init_path}"
|
61
65
|
end
|
62
66
|
|
67
|
+
#
|
68
|
+
# dumps the recent deploy history to the console
|
69
|
+
#
|
70
|
+
def history
|
71
|
+
run "(test -s /var/log/leap/deploy-summary.log && tail /var/log/leap/deploy-summary.log) || (test -s /var/log/leap/deploy-summary.log.1 && tail /var/log/leap/deploy-summary.log.1) || (echo 'no history')"
|
72
|
+
end
|
73
|
+
|
63
74
|
#
|
64
75
|
# This is a hairy ugly hack, exactly the kind of stuff that makes ruby
|
65
76
|
# dangerous and too much fun for its own good.
|
@@ -34,10 +34,11 @@ BAD_APT_GET_UPDATE = /(BADSIG|NO_PUBKEY|KEYEXPIRED|REVKEYSIG|NODATA)/
|
|
34
34
|
|
35
35
|
task :install_prerequisites, :max_hosts => MAX_HOSTS do
|
36
36
|
apt_get = "DEBIAN_FRONTEND=noninteractive apt-get -q -y -o DPkg::Options::=--force-confold"
|
37
|
+
apt_get_update = "apt-get update -o Acquire::Languages=none"
|
37
38
|
leap.mkdirs Leap::Platform.leap_dir
|
38
39
|
run "echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen"
|
39
40
|
leap.log :updating, "package list" do
|
40
|
-
run
|
41
|
+
run apt_get_update do |channel, stream, data|
|
41
42
|
# sadly exitcode is unreliable measure if apt-get update hit a failure.
|
42
43
|
if data =~ BAD_APT_GET_UPDATE
|
43
44
|
LeapCli::Util.bail! do
|
@@ -57,7 +58,7 @@ task :install_prerequisites, :max_hosts => MAX_HOSTS do
|
|
57
58
|
run "( test -f /etc/init.d/ntp && /etc/init.d/ntp start ) || true"
|
58
59
|
end
|
59
60
|
leap.log :installing, "required packages" do
|
60
|
-
run
|
61
|
+
run %[#{apt_get} install #{leap.required_wheezy_packages}]
|
61
62
|
end
|
62
63
|
#run "locale-gen"
|
63
64
|
leap.mkdirs("/etc/leap", "/srv/leap")
|
data/lib/leap_cli/util.rb
CHANGED
@@ -97,6 +97,23 @@ module LeapCli
|
|
97
97
|
return output
|
98
98
|
end
|
99
99
|
|
100
|
+
def assert_config!(conf_path)
|
101
|
+
value = nil
|
102
|
+
begin
|
103
|
+
value = manager.instance_eval(conf_path)
|
104
|
+
#rescue NoMethodError
|
105
|
+
#rescue NameError
|
106
|
+
ensure
|
107
|
+
assert! !value.nil? && value != "REQUIRED" do
|
108
|
+
log :missing, "required configuration value for #{conf_path}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
## FILES AND DIRECTORIES
|
115
|
+
##
|
116
|
+
|
100
117
|
def assert_files_missing!(*files)
|
101
118
|
options = files.last.is_a?(Hash) ? files.pop : {}
|
102
119
|
base = options[:base] || Path.provider
|
@@ -117,19 +134,6 @@ module LeapCli
|
|
117
134
|
end
|
118
135
|
end
|
119
136
|
|
120
|
-
def assert_config!(conf_path)
|
121
|
-
value = nil
|
122
|
-
begin
|
123
|
-
value = manager.instance_eval(conf_path)
|
124
|
-
#rescue NoMethodError
|
125
|
-
#rescue NameError
|
126
|
-
ensure
|
127
|
-
assert! !value.nil? && value != "REQUIRED" do
|
128
|
-
log :missing, "required configuration value for #{conf_path}"
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
137
|
def assert_files_exist!(*files)
|
134
138
|
options = files.last.is_a?(Hash) ? files.pop : {}
|
135
139
|
file_list = files.collect { |file_path|
|
@@ -149,6 +153,7 @@ module LeapCli
|
|
149
153
|
end
|
150
154
|
end
|
151
155
|
|
156
|
+
# takes a list of symbolic paths. returns true if all files exist or are directories.
|
152
157
|
def file_exists?(*files)
|
153
158
|
files.each do |file_path|
|
154
159
|
file_path = Path.named_path(file_path)
|
@@ -159,9 +164,16 @@ module LeapCli
|
|
159
164
|
return true
|
160
165
|
end
|
161
166
|
|
162
|
-
|
163
|
-
|
164
|
-
|
167
|
+
# takes a list of symbolic paths. returns true if all are directories.
|
168
|
+
def dir_exists?(*dirs)
|
169
|
+
dirs.each do |dir_path|
|
170
|
+
dir_path = Path.named_path(dir_path)
|
171
|
+
if !Dir.exists?(dir_path)
|
172
|
+
return false
|
173
|
+
end
|
174
|
+
end
|
175
|
+
return true
|
176
|
+
end
|
165
177
|
|
166
178
|
#
|
167
179
|
# creates a directory if it doesn't already exist
|
@@ -343,16 +355,12 @@ module LeapCli
|
|
343
355
|
end
|
344
356
|
|
345
357
|
#
|
346
|
-
# compares md5 fingerprints to see if the contents of a file match the
|
358
|
+
# compares md5 fingerprints to see if the contents of a file match the
|
359
|
+
# string we have in memory
|
347
360
|
#
|
348
361
|
def file_content_equals?(filepath, contents)
|
349
362
|
filepath = Path.named_path(filepath)
|
350
|
-
|
351
|
-
if $?.to_i == 0
|
352
|
-
return output.split(" ").first == Digest::MD5.hexdigest(contents).to_s
|
353
|
-
else
|
354
|
-
return false
|
355
|
-
end
|
363
|
+
Digest::MD5.file(filepath).hexdigest == Digest::MD5.hexdigest(contents)
|
356
364
|
end
|
357
365
|
|
358
366
|
##
|
data/lib/leap_cli/util/secret.rb
CHANGED
@@ -8,7 +8,7 @@ autoload :OpenSSL, 'openssl'
|
|
8
8
|
|
9
9
|
module LeapCli; module Util
|
10
10
|
class Secret
|
11
|
-
CHARS = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
|
11
|
+
CHARS = (('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a) - "i1loO06G".split(//u)
|
12
12
|
HEX = (0..9).to_a + ('a'..'f').to_a
|
13
13
|
|
14
14
|
#
|