knife-chop 0.2.6
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 +15 -0
- data/Gemfile +24 -0
- data/Gemfile.lock +154 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +23 -0
- data/Rakefile +54 -0
- data/TODO.rdoc +46 -0
- data/VERSION +1 -0
- data/bin/chop +48 -0
- data/knife-chop.gemspec +118 -0
- data/lib/chef/knife/chop/chef_data_bag_item.rb +43 -0
- data/lib/chef/knife/chop/chef_environment.rb +47 -0
- data/lib/chef/knife/chop/chef_knife.rb +85 -0
- data/lib/chef/knife/chop/chef_part.rb +191 -0
- data/lib/chef/knife/chop/chef_role.rb +48 -0
- data/lib/chef/knife/chop/cookbook_upload.rb +143 -0
- data/lib/chef/knife/chop/data_bag_from_file.rb +87 -0
- data/lib/chef/knife/chop/environment_from_file.rb +79 -0
- data/lib/chef/knife/chop/errors.rb +5 -0
- data/lib/chef/knife/chop/logging.rb +245 -0
- data/lib/chef/knife/chop/role_from_file.rb +45 -0
- data/lib/chef/knife/chop/translate.rb +23 -0
- data/lib/chef/knife/chop/translate/eden.rb +23 -0
- data/lib/chef/knife/chop/translate/rbeautify.rb +24 -0
- data/lib/chef/knife/chop/ui.rb +110 -0
- data/lib/chef/knife/chop/version.rb +9 -0
- data/lib/chef/knife/chop_base.rb +821 -0
- data/lib/chef/knife/chop_translate.rb +161 -0
- data/lib/chef/knife/chop_upload.rb +199 -0
- data/lib/ruby-beautify/Gemfile +4 -0
- data/lib/ruby-beautify/LICENSE +22 -0
- data/lib/ruby-beautify/README.md +39 -0
- data/lib/ruby-beautify/RELEASE.md +13 -0
- data/lib/ruby-beautify/Rakefile +2 -0
- data/lib/ruby-beautify/bin/rbeautify +28 -0
- data/lib/ruby-beautify/lib/beautifier.rb +168 -0
- data/lib/ruby-beautify/lib/ruby-beautify.rb +26 -0
- data/lib/ruby-beautify/lib/ruby-beautify/block_end.rb +23 -0
- data/lib/ruby-beautify/lib/ruby-beautify/block_matcher.rb +153 -0
- data/lib/ruby-beautify/lib/ruby-beautify/block_start.rb +119 -0
- data/lib/ruby-beautify/lib/ruby-beautify/config/ruby.rb +131 -0
- data/lib/ruby-beautify/lib/ruby-beautify/language.rb +37 -0
- data/lib/ruby-beautify/lib/ruby-beautify/line.rb +53 -0
- data/lib/ruby-beautify/lib/ruby-beautify/version.rb +3 -0
- data/lib/ruby-beautify/ruby-beautify.gemspec +17 -0
- data/lib/ruby-beautify/spec/fixtures/ruby.yml +408 -0
- data/lib/ruby-beautify/spec/rbeautify/block_matcher_spec.rb +89 -0
- data/lib/ruby-beautify/spec/rbeautify/block_start_spec.rb +51 -0
- data/lib/ruby-beautify/spec/rbeautify/config/ruby_spec.rb +183 -0
- data/lib/ruby-beautify/spec/rbeautify/line_spec.rb +73 -0
- data/lib/ruby-beautify/spec/rbeautify_spec.rb +1 -0
- data/lib/ruby-beautify/spec/spec_helper.rb +124 -0
- data/spec/knife-chop_spec.rb +7 -0
- data/spec/spec_helper.rb +12 -0
- metadata +233 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christo De Lange (<opscode@dldinternet.com>)
|
3
|
+
# Copyright:: Copyright (c) 2013 DLDInternet, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'json'
|
20
|
+
#require 'chef/knife/chop/translate/eden'
|
21
|
+
require 'chef/knife/chop/translate/rbeautify'
|
22
|
+
#require 'ripper'
|
23
|
+
#require 'sorcerer'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christo De Lange (<opscode@dldinternet.com>)
|
3
|
+
# Copyright:: Copyright (c) 2013 DLDInternet, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
path = File.expand_path(File.dirname(__FILE__)+"/../../../../")
|
20
|
+
path = File.expand_path("eden/lib", path)
|
21
|
+
$:.unshift(path)
|
22
|
+
|
23
|
+
require 'eden'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christo De Lange (<opscode@dldinternet.com>)
|
3
|
+
# Copyright:: Copyright (c) 2013 DLDInternet, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
path = File.expand_path(File.dirname(__FILE__)+"/../../../../")
|
20
|
+
path = File.expand_path("ruby-beautify/lib", path)
|
21
|
+
$:.unshift(path)
|
22
|
+
|
23
|
+
require 'ruby-beautify'
|
24
|
+
include RBeautify
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christo De Lange (<opscode@dldinternet.com>)
|
3
|
+
# Copyright:: Copyright (c) 2013 DLDInternet, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef/knife/core/ui'
|
20
|
+
require 'chef/knife/chop/errors'
|
21
|
+
|
22
|
+
class Chef
|
23
|
+
class Knife
|
24
|
+
class ChopUI < ::Chef::Knife::UI
|
25
|
+
include ChopErrors
|
26
|
+
|
27
|
+
attr_reader :logger
|
28
|
+
|
29
|
+
def initialize(logger, config)
|
30
|
+
super($stdout, $stderr, $stdin, config)
|
31
|
+
@logger = logger
|
32
|
+
#define_ui_methods()
|
33
|
+
end
|
34
|
+
|
35
|
+
#def define_log_methods( ui )
|
36
|
+
def msg(message)
|
37
|
+
caller = Kernel.caller[0]
|
38
|
+
match = %r/([-\.\/\(\)\w]+):(\d+)(?::in `(\w+)')?/o.match(caller)
|
39
|
+
name = shifted(match[3])
|
40
|
+
@logger.send(name, message)
|
41
|
+
end
|
42
|
+
|
43
|
+
#def define_ui_methods()
|
44
|
+
# class << self
|
45
|
+
# ::Logging::LEVELS.each{|name,level|
|
46
|
+
# code = <<-CODE
|
47
|
+
# def #{name}(str)
|
48
|
+
# msg(str)
|
49
|
+
# end
|
50
|
+
# CODE
|
51
|
+
# self.class.class_eval(code,__FILE__,__LINE__)
|
52
|
+
# }
|
53
|
+
# end
|
54
|
+
#end
|
55
|
+
def info(message)
|
56
|
+
msg(message)
|
57
|
+
end
|
58
|
+
|
59
|
+
def step(message)
|
60
|
+
msg(message)
|
61
|
+
end
|
62
|
+
|
63
|
+
def err(message)
|
64
|
+
error(message)
|
65
|
+
end
|
66
|
+
|
67
|
+
def error(message)
|
68
|
+
msg(message)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Print a warning message
|
72
|
+
def warn(message)
|
73
|
+
msg(message)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Print an error message
|
77
|
+
def error(message)
|
78
|
+
msg(message)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Print a message describing a fatal error.
|
82
|
+
def fatal(message)
|
83
|
+
msg(message)
|
84
|
+
end
|
85
|
+
|
86
|
+
def method_missing(name, *args, &block)
|
87
|
+
msg = "#{self.class.name}: Method missing: #{name}"
|
88
|
+
@logger.fatal(msg)
|
89
|
+
raise ChopInternalError.new(msg)
|
90
|
+
end
|
91
|
+
|
92
|
+
def shifted(name)
|
93
|
+
num = ::Logging::LEVELS[name]+1
|
94
|
+
case name
|
95
|
+
when 'todo'
|
96
|
+
'error'
|
97
|
+
when 'err'
|
98
|
+
'error'
|
99
|
+
when 'info'
|
100
|
+
'debug'
|
101
|
+
when 'debug'
|
102
|
+
'trace'
|
103
|
+
else
|
104
|
+
name
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,821 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christo De Lange (<opscode@dldinternet.com>)
|
3
|
+
# Copyright:: Copyright (c) 2013 DLDInternet, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require "awesome_print"
|
20
|
+
require 'chef/knife'
|
21
|
+
require 'chef/knife/chop/version'
|
22
|
+
require 'chef/knife/chop/logging'
|
23
|
+
require 'chef/knife/chop/errors'
|
24
|
+
require 'logging'
|
25
|
+
|
26
|
+
class Chef
|
27
|
+
class Knife
|
28
|
+
attr_accessor :logger
|
29
|
+
attr_accessor :verbosity
|
30
|
+
attr_accessor :LOGLEVELS
|
31
|
+
attr_accessor :ALLPARTS
|
32
|
+
attr_accessor :ALLACTIONS
|
33
|
+
|
34
|
+
def self.loglevels=(levels)
|
35
|
+
@LOGLEVELS = levels || [:trace, :debug, :step, :info, :warn, :error, :fatal, :todo]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.allparts=(parts)
|
39
|
+
@ALLPARTS = parts || [:environments, :roles, :databags, :cookbooks]
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.allactions=(acts)
|
43
|
+
@ALLACTIONS = acts || [:upload, :translate]
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.loglevels
|
47
|
+
@LOGLEVELS
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.allparts
|
51
|
+
@ALLPARTS
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.allactions
|
55
|
+
@ALLACTIONS
|
56
|
+
end
|
57
|
+
|
58
|
+
self.loglevels = nil
|
59
|
+
self.allparts = nil
|
60
|
+
self.allactions = nil
|
61
|
+
|
62
|
+
|
63
|
+
module ChopBase
|
64
|
+
class ::TrueClass
|
65
|
+
def to_rb
|
66
|
+
to_s
|
67
|
+
end
|
68
|
+
def yesno
|
69
|
+
"yes"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ::FalseClass
|
74
|
+
def to_rb
|
75
|
+
to_s
|
76
|
+
end
|
77
|
+
def yesno
|
78
|
+
"no"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
include ChopErrors
|
83
|
+
|
84
|
+
include ChopLogging
|
85
|
+
|
86
|
+
# --------------------------------------------------------------------------------
|
87
|
+
def parsePartSymbol(v)
|
88
|
+
if v.to_sym == :all
|
89
|
+
::Chef::Knife.allparts
|
90
|
+
else
|
91
|
+
s = v.to_sym
|
92
|
+
allparts = [::Chef::Knife.allparts, :all].flatten
|
93
|
+
unless allparts.include?(s)
|
94
|
+
allparts.each{ |p|
|
95
|
+
s = p if p.match(%r/^#{s}/)
|
96
|
+
}
|
97
|
+
end
|
98
|
+
s = ::Chef::Knife.allparts if s == :all
|
99
|
+
s
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# --------------------------------------------------------------------------------
|
104
|
+
def parseActionSymbol(v)
|
105
|
+
if v.to_sym == :all
|
106
|
+
::Chef::Knife.allactions
|
107
|
+
else
|
108
|
+
s = v.to_sym
|
109
|
+
allactions = [::Chef::Knife.allactions, :all].flatten
|
110
|
+
unless allactions.include?(s)
|
111
|
+
allactions.each{ |p|
|
112
|
+
s = p if p.match(%r/^#{s}/)
|
113
|
+
}
|
114
|
+
end
|
115
|
+
s = ::Chef::Knife.allactions if s == :all
|
116
|
+
s
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# --------------------------------------------------------------------------------
|
121
|
+
def parseString(v)
|
122
|
+
v
|
123
|
+
end
|
124
|
+
|
125
|
+
# --------------------------------------------------------------------------------
|
126
|
+
def parsePath(v)
|
127
|
+
File.expand_path(parseString(v))
|
128
|
+
end
|
129
|
+
|
130
|
+
# --------------------------------------------------------------------------------
|
131
|
+
def parseList(v,s=',',method='parseString')
|
132
|
+
parts = []
|
133
|
+
a = v.split(%r/#{s}/)
|
134
|
+
a.each{ |t|
|
135
|
+
parts << send(method,t)
|
136
|
+
}
|
137
|
+
parts
|
138
|
+
end
|
139
|
+
|
140
|
+
# --------------------------------------------------------------------------------
|
141
|
+
def parseOptionString(v,s=',',method='parseString')
|
142
|
+
bags = []
|
143
|
+
if v.match(%r'#{s}')
|
144
|
+
bags << parseList(v,s,method)
|
145
|
+
else
|
146
|
+
bags << send(method,v)
|
147
|
+
end
|
148
|
+
bags.flatten
|
149
|
+
end
|
150
|
+
|
151
|
+
# --------------------------------------------------------------------------------
|
152
|
+
def parsePrecedence(v)
|
153
|
+
::Chef::Knife.prec_max += 1
|
154
|
+
s = { v => ::Chef::Knife.prec_max }
|
155
|
+
match = v.match(%r/^(\S+):(\d+)$/)
|
156
|
+
if match
|
157
|
+
begin
|
158
|
+
a = match[1]
|
159
|
+
i = match[2].to_i
|
160
|
+
s = { a => i }
|
161
|
+
rescue => e
|
162
|
+
puts "ERROR: Unable to match precedence #{v}"
|
163
|
+
raise e
|
164
|
+
end
|
165
|
+
end
|
166
|
+
s
|
167
|
+
end
|
168
|
+
|
169
|
+
# --------------------------------------------------------------------------------
|
170
|
+
def self.included(includer)
|
171
|
+
includer.class_eval do
|
172
|
+
|
173
|
+
deps do
|
174
|
+
require 'fog'
|
175
|
+
require 'readline'
|
176
|
+
require 'colorize'
|
177
|
+
require 'inifile'
|
178
|
+
require 'chef/knife/chop/chef_knife'
|
179
|
+
require 'chef/environment'
|
180
|
+
require 'chef/knife/core/object_loader'
|
181
|
+
require 'chef/cookbook_loader'
|
182
|
+
require 'chef/cookbook_uploader'
|
183
|
+
require 'chef/knife/chop/cookbook_upload'
|
184
|
+
require 'chef/knife/chop/data_bag_from_file'
|
185
|
+
require 'chef/knife/chop/role_from_file'
|
186
|
+
require 'chef/knife/chop/environment_from_file'
|
187
|
+
require 'chef/knife/chop/chef_part'
|
188
|
+
require 'chef/knife/chop/chef_environment'
|
189
|
+
require 'chef/knife/chop/chef_role'
|
190
|
+
#require 'chef/knife/chop/chef_data_bag_item'
|
191
|
+
require 'chef/json_compat'
|
192
|
+
require 'chef/knife/bootstrap'
|
193
|
+
require 'chef/knife/chop/ui'
|
194
|
+
Chef::Knife::Bootstrap.load_deps
|
195
|
+
end
|
196
|
+
|
197
|
+
banner "knife chop (options)"
|
198
|
+
|
199
|
+
attr_reader :argv
|
200
|
+
|
201
|
+
# This will print an args summary.
|
202
|
+
option :help,
|
203
|
+
:short => "-h",
|
204
|
+
:long => "--help",
|
205
|
+
:description => "Show this message",
|
206
|
+
:show_options => true,
|
207
|
+
:exit => 1
|
208
|
+
# print the version.
|
209
|
+
option :version,
|
210
|
+
:short => '-V',
|
211
|
+
:long => "--version",
|
212
|
+
:description => "Show version",
|
213
|
+
:proc => Proc.new{ puts ::Knife::Chop::VERSION },
|
214
|
+
:exit => 2
|
215
|
+
option :verbosity,
|
216
|
+
:short => "-v",
|
217
|
+
:long => "--[no-]verbose [LEVEL]",
|
218
|
+
:description => "Run verbosely",
|
219
|
+
:proc => lambda{|s|
|
220
|
+
if s.nil? or (s == '')
|
221
|
+
$CHOP.verbosity += 1
|
222
|
+
else
|
223
|
+
$CHOP.verbosity = v.gsub(%r/['"]*/, '').to_i
|
224
|
+
end
|
225
|
+
}
|
226
|
+
option :log_path,
|
227
|
+
:long => '--log-path PATH',
|
228
|
+
:description => "Log destination path"
|
229
|
+
option :log_file,
|
230
|
+
:long => '--log-file PATH',
|
231
|
+
:description => "Log destination file"
|
232
|
+
option :log_level,
|
233
|
+
:short => '-l',
|
234
|
+
:long => ['--log_level LEVEL','--log-level LEVEL'],
|
235
|
+
:description => "Log level (#{::Chef::Knife.loglevels.to_s})",
|
236
|
+
:proc => lambda{|v|
|
237
|
+
if ::Chef::Knife.loglevels.include? v.to_sym
|
238
|
+
v.to_sym
|
239
|
+
else
|
240
|
+
level = ::Chef::Knife.loglevels.select{|l| l.to_s.match(%r(^#{v}))}
|
241
|
+
unless level.size > 0
|
242
|
+
raise OptionParser::InvalidOption.new("Invalid log level: #{v}. Valid levels are #{::Chef::Knife.loglevels.ai}")
|
243
|
+
end
|
244
|
+
level[0].to_sym
|
245
|
+
end
|
246
|
+
},
|
247
|
+
:default => :step
|
248
|
+
option :inifile,
|
249
|
+
:short => "-f",
|
250
|
+
:long => "--inifile FILE",
|
251
|
+
:description => "INI file with settings"
|
252
|
+
option :parts,
|
253
|
+
:short => "-R",
|
254
|
+
:long => "--resources PARTS",
|
255
|
+
:description => "Parts to upload #{[ :all, ::Chef::Knife.allparts].flatten }. Default: all",
|
256
|
+
:default => ::Chef::Knife.allparts,
|
257
|
+
:proc => lambda{|v|
|
258
|
+
parts = $CHOP.parseOptionString(v,',','parsePartSymbol')
|
259
|
+
parts.each{ |part|
|
260
|
+
raise ::OptionParser::InvalidOption.new("Invalid part: #{part.to_s}. Valid parts are: #{[::Chef::Knife.allparts, :all].to_s}") unless [::Chef::Knife.allparts, :all].flatten.include?(part.to_sym)
|
261
|
+
}
|
262
|
+
parts
|
263
|
+
}
|
264
|
+
option :depends,
|
265
|
+
:short => "-I",
|
266
|
+
:long => "--[no-]include-dependencies [yes|no|true|false|0|1|enable|disable]",
|
267
|
+
:description => "Include Cookbook dependencies?, Default --include-dependencies or -I [1|yes|enable|true]",
|
268
|
+
:default => true
|
269
|
+
option :dry_run,
|
270
|
+
:short => "-n",
|
271
|
+
:long => "--[no-]dry-run",
|
272
|
+
:description => "Do a dry run, Default --no-dry-run",
|
273
|
+
:default => false
|
274
|
+
option :cookbook_path,
|
275
|
+
:short => "-P",
|
276
|
+
:long => "--cookbook-path PATH",
|
277
|
+
:description => "Cookbook search path, Default chef/cookbooks/:chef/vendor-cookbooks",
|
278
|
+
:default => ["cookbooks/","vendor-cookbooks"],
|
279
|
+
:proc => lambda{|v|
|
280
|
+
$CHOP.parseOptionString(v,'[:,]','parsePath')
|
281
|
+
}
|
282
|
+
option :repo_path,
|
283
|
+
:long => "--repo-path PATH",
|
284
|
+
:description => "Chef repo path, Default ./chef",
|
285
|
+
:default => "./chef",
|
286
|
+
#:required => true,
|
287
|
+
:proc => lambda{|v|
|
288
|
+
File.expand_path(v)
|
289
|
+
}
|
290
|
+
option :cookbooks,
|
291
|
+
:short => "-c",
|
292
|
+
:long => "--cookbooks COOKBOOKS",
|
293
|
+
:description => "Cookbooks to upload (List separated by commas or --all. Default: role",
|
294
|
+
:default => ['role'],
|
295
|
+
:proc => lambda{|v|
|
296
|
+
$CHOP.parseOptionString(v)
|
297
|
+
}
|
298
|
+
option :envs,
|
299
|
+
:short => ["-e", "-E",],
|
300
|
+
:long => "--environments REGEXLIST",
|
301
|
+
:description => "Environments regex",
|
302
|
+
:proc => lambda{|v|
|
303
|
+
$CHOP.parseOptionString(v)
|
304
|
+
},
|
305
|
+
:default => ['web.*']
|
306
|
+
option :databags,
|
307
|
+
:short => "-b",
|
308
|
+
:long => "--databags BAGS",
|
309
|
+
:description => "Data bags to upload",
|
310
|
+
:default => ['aws:s3_.*_dev;s3_ro_.*','users:web.*;christo.*;tmiller.*'],
|
311
|
+
:proc => lambda{|v|
|
312
|
+
$CHOP.parseOptionString(v)
|
313
|
+
}
|
314
|
+
option :roles,
|
315
|
+
:short => "-r",
|
316
|
+
:long => "--roles ROLES",
|
317
|
+
:description => "Roles to upload",
|
318
|
+
:default => ["web.*"],
|
319
|
+
:proc => lambda{|v|
|
320
|
+
$CHOP.parseOptionString(v)
|
321
|
+
}
|
322
|
+
|
323
|
+
option :all,
|
324
|
+
:short => "-a",
|
325
|
+
:long => "--all",
|
326
|
+
:description => "Upload all items for resource group(s)"
|
327
|
+
|
328
|
+
option :trace,
|
329
|
+
:short => "-t",
|
330
|
+
:long => "--trace",
|
331
|
+
:boolean => true,
|
332
|
+
:default => false,
|
333
|
+
:description => "Trace logging locations (file::line)"
|
334
|
+
|
335
|
+
# ------------------------------------------------------------------------------------------------------------
|
336
|
+
# Cookbooks
|
337
|
+
# ------------------------------------------------------------------------------------------------------------
|
338
|
+
option :freeze,
|
339
|
+
:long => '--freeze',
|
340
|
+
:description => 'Freeze this version of the cookbook so that it cannot be overwritten',
|
341
|
+
:boolean => true
|
342
|
+
|
343
|
+
#option :all,
|
344
|
+
# :short => "-a",
|
345
|
+
# :long => "--all",
|
346
|
+
# :description => "Upload all cookbooks, rather than just a single cookbook"
|
347
|
+
|
348
|
+
option :force,
|
349
|
+
:long => '--force',
|
350
|
+
:boolean => true,
|
351
|
+
:description => "Update cookbook versions even if they have been frozen"
|
352
|
+
|
353
|
+
# ------------------------------------------------------------------------------------------------------------
|
354
|
+
# Data bags
|
355
|
+
# ------------------------------------------------------------------------------------------------------------
|
356
|
+
option :secret,
|
357
|
+
:short => "-s SECRET",
|
358
|
+
:long => "--secret ",
|
359
|
+
:description => "The secret key to use to encrypt data bag item values"
|
360
|
+
|
361
|
+
option :secret_file,
|
362
|
+
:long => "--secret-file SECRET_FILE",
|
363
|
+
:description => "A file containing the secret key to use to encrypt data bag item values"
|
364
|
+
|
365
|
+
|
366
|
+
# ------------------------------------------------------------------------------------------------------------
|
367
|
+
option :precedence,
|
368
|
+
:long => "--precedence PREC",
|
369
|
+
:description => "Precedence order of parts extensions. Default: json:1,rb:2 or json,rb == [json rb] == { json => 1, rb => 2 } == .rb files will be used when there is both a .json and .rb",
|
370
|
+
:default => %w(json rb),
|
371
|
+
:proc => lambda{|v|
|
372
|
+
prec = $CHOP.parseOptionString(v,',', 'parsePrecedence')
|
373
|
+
prec.sort{|x,y| x.values.shift <=> y.values.shift }.map{|e| e.keys.shift }
|
374
|
+
}
|
375
|
+
option :actions,
|
376
|
+
:short => '-a',
|
377
|
+
:long => "--action ACTION",
|
378
|
+
:description => "Actions to be performed #{[ :all, ::Chef::Knife.allactions].flatten }. Default: upload",
|
379
|
+
#:default => [:upload],
|
380
|
+
:proc => lambda{|v|
|
381
|
+
actions = $CHOP.parseOptionString(v,',','parseActionSymbol')
|
382
|
+
actions.each{ |act|
|
383
|
+
raise ::OptionParser::InvalidOption.new("Invalid action: #{act.to_s}. Valid actions are: #{[ :all, ::Chef::Knife.allactions].flatten.to_s}") unless [ :all, ::Chef::Knife.allactions].flatten.include?(act.to_sym)
|
384
|
+
}
|
385
|
+
actions
|
386
|
+
}
|
387
|
+
option :translate,
|
388
|
+
:long => "--translate PREC",
|
389
|
+
:description => "Translate parts. Default: json,rb == { :from => 'json', :to => 'rb' } == .json files will be read and .rb equivalents will be saved",
|
390
|
+
:default => %w(json rb),
|
391
|
+
:proc => lambda{|v|
|
392
|
+
$CHOP.parseOptionString(v)
|
393
|
+
}
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
# --------------------------------------------------------------------------------
|
398
|
+
# Create a new instance of the current class configured for the given
|
399
|
+
# arguments and options
|
400
|
+
def initialize(argv=[])
|
401
|
+
@argv = argv
|
402
|
+
$CHOP = self
|
403
|
+
@verbosity = 0
|
404
|
+
@inis = []
|
405
|
+
@use_knife_api = true
|
406
|
+
|
407
|
+
@stop = false
|
408
|
+
@prec_max = 0
|
409
|
+
@TODO = {}
|
410
|
+
@actors = {}
|
411
|
+
|
412
|
+
super
|
413
|
+
end
|
414
|
+
|
415
|
+
# --------------------------------------------------------------------------------
|
416
|
+
def build_option_arguments(opt_setting)
|
417
|
+
arguments = super
|
418
|
+
arguments.flatten
|
419
|
+
end
|
420
|
+
|
421
|
+
# --------------------------------------------------------------------------------
|
422
|
+
def parse_options(args,source=nil)
|
423
|
+
argv = super(args)
|
424
|
+
|
425
|
+
@config = parse_and_validate_options(@config,source ? source : "ARGV - #{__LINE__}")
|
426
|
+
v = @config[:depends]
|
427
|
+
@config[:depends] = (v === true) || ((v.is_a?(String) && v.downcase.match(%r/^(no|false|disable|0)/) ).nil? ? false : true)
|
428
|
+
|
429
|
+
unless @config[:actions]
|
430
|
+
@config[:actions] = [ argv[1].to_sym ] # self.class.name.gsub(%r(Chef::Knife::Chop), '').downcase
|
431
|
+
end
|
432
|
+
@actors[argv[1].to_sym] = self
|
433
|
+
others = @config[:actions].select{|a|
|
434
|
+
a != argv[1].to_sym
|
435
|
+
}
|
436
|
+
index = args.index '--action'
|
437
|
+
others.each{|a|
|
438
|
+
args[1] = a.to_s
|
439
|
+
unless index.nil?
|
440
|
+
args[index+1] = a.to_s
|
441
|
+
end
|
442
|
+
subcommand_class = ::Chef::Knife.subcommand_class_from(args)
|
443
|
+
subcommand_class.load_deps
|
444
|
+
instance = subcommand_class.new(args)
|
445
|
+
instance.configure_chef
|
446
|
+
@actors[a] = instance
|
447
|
+
}
|
448
|
+
argv
|
449
|
+
end
|
450
|
+
|
451
|
+
module ::Logging
|
452
|
+
class << self
|
453
|
+
def levelnames=(lnames)
|
454
|
+
remove_const(:LNAMES)
|
455
|
+
const_set(:LNAMES, lnames)
|
456
|
+
end
|
457
|
+
def levelnames()
|
458
|
+
LNAMES
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
# --------------------------------------------------------------------------------
|
464
|
+
def configure_chef
|
465
|
+
super
|
466
|
+
@config[:log_opts] = lambda{|mlll| {
|
467
|
+
:pattern => "%#{mlll}l: %m %C\n",
|
468
|
+
:date_pattern => '%Y-%m-%d %H:%M:%S',
|
469
|
+
}
|
470
|
+
}
|
471
|
+
|
472
|
+
@logger = getLogger(@config)
|
473
|
+
@ui = Chef::Knife::ChopUI.new(@logger,@config)
|
474
|
+
Chef::Log.logger = @logger
|
475
|
+
end
|
476
|
+
|
477
|
+
def run_with_pretty_exceptions
|
478
|
+
unless self.respond_to?(:run)
|
479
|
+
ui.error "You need to add a #run method to your knife command before you can use it"
|
480
|
+
end
|
481
|
+
enforce_path_sanity
|
482
|
+
|
483
|
+
raise ChopOptionError.new("The --repo-path '#{@config[:repo_path]}' is invalid!") unless File.directory?(@config[:repo_path])
|
484
|
+
|
485
|
+
run
|
486
|
+
rescue ChopOptionError => e
|
487
|
+
raise if Chef::Config[:verbosity] == 2
|
488
|
+
humanize_exception(e)
|
489
|
+
exit 100
|
490
|
+
rescue ChopError => e
|
491
|
+
humanize_exception(e)
|
492
|
+
exit 101
|
493
|
+
end
|
494
|
+
|
495
|
+
# --------------------------------------------------------------------------------
|
496
|
+
private
|
497
|
+
# --------------------------------------------------------------------------------
|
498
|
+
|
499
|
+
# --------------------------------------------------------------------------------
|
500
|
+
def parseINIFile(options=nil)
|
501
|
+
options = @config unless options
|
502
|
+
if options.key?(:inifile)
|
503
|
+
logStep "Parse INI file - #{options[:inifile]}"
|
504
|
+
raise ChopError.new("Cannot find inifile (#{options[:inifile]})") unless File.exist?(options[:inifile])
|
505
|
+
raise ChopError.new("Recursive call to inifile == '#{options[:inifile]}'") if @inis.include?(options[:inifile])
|
506
|
+
ini = nil
|
507
|
+
begin
|
508
|
+
ini = IniFile.load(options[:inifile])
|
509
|
+
@inis << options[:inifile]
|
510
|
+
ini['global'].each { |key, value|
|
511
|
+
#puts "#{key}=#{value}"
|
512
|
+
ENV[key]=value
|
513
|
+
}
|
514
|
+
argv=[]
|
515
|
+
cli = ini['cli'] || []
|
516
|
+
cli.each{ |key,value|
|
517
|
+
argv << key.gsub(%r/:[0-9]+$/, '').gsub(%r/^([^-])/, '--\1')
|
518
|
+
argv << value
|
519
|
+
}
|
520
|
+
if argv.size > 0
|
521
|
+
parse_options(argv,"INI-#{options[:inifile]}")
|
522
|
+
end
|
523
|
+
rescue => e
|
524
|
+
puts e.message.light_red
|
525
|
+
raise e
|
526
|
+
end
|
527
|
+
end
|
528
|
+
options
|
529
|
+
end
|
530
|
+
|
531
|
+
# -----------------------------------------------------------------------------
|
532
|
+
def setDefaultOptions(options)
|
533
|
+
@options.each{|name,args|
|
534
|
+
if args[:default]
|
535
|
+
options[name] = args[:default] unless options[name]
|
536
|
+
end
|
537
|
+
}
|
538
|
+
setOrigins(options,'default')
|
539
|
+
end
|
540
|
+
|
541
|
+
# -----------------------------------------------------------------------------
|
542
|
+
def validate_options(options=nil)
|
543
|
+
options = @config unless options
|
544
|
+
|
545
|
+
# Check for the necessary environment variables
|
546
|
+
logStep ("Check ENVironment")
|
547
|
+
env = ENV.to_hash
|
548
|
+
missing = {}
|
549
|
+
%w(AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY KNIFE_CHEF_SERVER_URL KNIFE_CLIENT_KEY KNIFE_CLIENT_NAME).each { |k|
|
550
|
+
missing[k] = true unless ENV.has_key?(k)
|
551
|
+
}
|
552
|
+
|
553
|
+
if missing.count() > 0
|
554
|
+
#@logger.error "Missing keys: #{missing.keys.ai}"
|
555
|
+
raise ChopError.new("Missing environment variables: #{missing.keys}")
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
# -----------------------------------------------------------------------------
|
560
|
+
def parse_and_validate_options(options=nil,source='ARGV')
|
561
|
+
options = @config unless options
|
562
|
+
setOrigins(options,source)
|
563
|
+
|
564
|
+
#options = parseOptions(options,source)
|
565
|
+
unless @origins and @name_key_map
|
566
|
+
# These are the essential default options which things like parseOptions depend on
|
567
|
+
{
|
568
|
+
:verbosity => @verbosity,
|
569
|
+
:auto_purge => false,
|
570
|
+
}.each{ |k,v|
|
571
|
+
options[k] = v unless options[k]
|
572
|
+
}
|
573
|
+
setOrigins(options,'hardcoded-default')
|
574
|
+
|
575
|
+
@name_key_map = {} unless @name_key_map
|
576
|
+
@options.each{ |name,args|
|
577
|
+
@name_key_map[name] = {} unless @name_key_map[name]
|
578
|
+
[:short,:long,:description].each{|key|
|
579
|
+
@name_key_map[name][key] = args[key] if args[key]
|
580
|
+
}
|
581
|
+
}
|
582
|
+
end
|
583
|
+
|
584
|
+
begin
|
585
|
+
parseINIFile(options)
|
586
|
+
setDefaultOptions(options)
|
587
|
+
# Check for all the necessary options
|
588
|
+
validate_options(options)
|
589
|
+
checkArgsSources(options)
|
590
|
+
#findRootPath(options)
|
591
|
+
rescue ChopError => e
|
592
|
+
puts e.message.light_red
|
593
|
+
exit -1
|
594
|
+
rescue Exception => e
|
595
|
+
puts e.message.light_red
|
596
|
+
exit -2
|
597
|
+
end
|
598
|
+
|
599
|
+
options
|
600
|
+
end
|
601
|
+
|
602
|
+
# ---------------------------------------------------------------------------------------------------------------
|
603
|
+
def setOrigins(options,source)
|
604
|
+
@origins = {} unless @origins
|
605
|
+
options.each { |key, val|
|
606
|
+
@origins[key] = source unless (@origins[key])
|
607
|
+
}
|
608
|
+
end
|
609
|
+
|
610
|
+
# ---------------------------------------------------------------------------------------------------------------
|
611
|
+
def checkArgsSources(options)
|
612
|
+
if @origins
|
613
|
+
missing = @origins.select{ |k,v|
|
614
|
+
v.nil?
|
615
|
+
}.map{ |k,v| k }
|
616
|
+
raise ChopError.new("Missing origins: #{missing.ai}") if missing.size > 0
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
## ---------------------------------------------------------------------------------------------------------------
|
621
|
+
#def findRootPath(options)
|
622
|
+
# @root_path = ''
|
623
|
+
# cbpaths = @config[:cookbook_path]#.split(File::PATH_SEPARATOR)
|
624
|
+
# common = cbpaths[0]
|
625
|
+
# begin
|
626
|
+
# common = File.dirname(common)
|
627
|
+
# ayes = 1
|
628
|
+
# cbpaths[1..-1].each{ |cbp|
|
629
|
+
# if File.dirname(cbp).match(%r(^#{common}))
|
630
|
+
# ayes += 1
|
631
|
+
# end
|
632
|
+
# }
|
633
|
+
# @root_path = common
|
634
|
+
# end while ((common != '') and (ayes != cbpaths.size))
|
635
|
+
# @root_path
|
636
|
+
#end
|
637
|
+
|
638
|
+
# --------------------------------------------------------------------------------
|
639
|
+
def getPathSet(want, path, exts=nil)
|
640
|
+
raise ChopInternalError.new("Bad call to #{self.class.name}.getPathSet: want == nil") unless want
|
641
|
+
@logger.debug "Look for #{want.ai} in #{[path]} with #{exts} extensions"
|
642
|
+
if exts.nil?
|
643
|
+
exts = @config[:precedence]
|
644
|
+
end
|
645
|
+
file_regex=%r/^(\S+)\.(#{exts.join('|')})$/
|
646
|
+
if exts.empty?
|
647
|
+
file_regex=%r/^(\S+)()$/
|
648
|
+
exts=['']
|
649
|
+
end
|
650
|
+
regex = "^(#{want.join('|')})$"
|
651
|
+
set = {}
|
652
|
+
chef = @config[:repo_path]
|
653
|
+
raise ChopError.new "Oops! Where is the '#{chef}' directory? Also check cookbook path '#{@config[:cookbook_path]}'" unless File.directory?(chef)
|
654
|
+
abs = File.expand_path("#{chef}/#{path}")#.gsub(%r(^#{@chop_path}), '')
|
655
|
+
raise ChopError.new "Oops! Does 'chef/#{path}' directory exist?" unless File.directory?(abs)
|
656
|
+
Dir.glob("#{abs}/*").each{ |f|
|
657
|
+
match = File.basename(f).match(file_regex)
|
658
|
+
if match
|
659
|
+
name = match[1]
|
660
|
+
ext = match[2]
|
661
|
+
set[ext] = {} unless set[ext]
|
662
|
+
@logger.trace "#{name} =~ #{regex}"
|
663
|
+
set[ext][name] = f if name.match(regex)
|
664
|
+
end
|
665
|
+
}
|
666
|
+
@logger.debug "getPathSet set=#{set.ai}"
|
667
|
+
res = {}
|
668
|
+
# Iterate extension sets in increasing precedence order ...
|
669
|
+
# Survivor will be the most desireable version of the item
|
670
|
+
# i.e. the .rb environment, role, data bag, etc. will be preferred over the .json version
|
671
|
+
exts.each{ |e|
|
672
|
+
h = set[e]
|
673
|
+
if h
|
674
|
+
h.each{ |n,f|
|
675
|
+
@logger.warn "Ignoring #{File.basename(res[n])}" if res[n]
|
676
|
+
res[n] = f
|
677
|
+
}
|
678
|
+
else
|
679
|
+
@logger.warn "'#{e}' set is empty! (No #{path}/*.#{e} files found using precedence #{exts})"
|
680
|
+
end
|
681
|
+
}
|
682
|
+
set = res
|
683
|
+
set
|
684
|
+
end
|
685
|
+
|
686
|
+
# --------------------------------------------------------------------------------
|
687
|
+
def todo(msg)
|
688
|
+
|
689
|
+
# Regular expression used to parse out caller information
|
690
|
+
#
|
691
|
+
# * $1 == filename
|
692
|
+
# * $2 == line number
|
693
|
+
# * $3 == method name (might be nil)
|
694
|
+
caller_rgxp = %r/([-\.\/\(\)\w]+):(\d+)(?::in `(\w+)')?/o
|
695
|
+
#CALLER_INDEX = 2
|
696
|
+
caller_index = ((defined? JRUBY_VERSION and JRUBY_VERSION[%r/^1.6/]) or (defined? RUBY_ENGINE and RUBY_ENGINE[%r/^rbx/i])) ? 1 : 2
|
697
|
+
stack = Kernel.caller[caller_index]
|
698
|
+
return if stack.nil?
|
699
|
+
|
700
|
+
match = caller_rgxp.match(stack)
|
701
|
+
file = match[1]
|
702
|
+
line = Integer(match[2])
|
703
|
+
modl = match[3] unless match[3].nil?
|
704
|
+
|
705
|
+
unless @TODO[line]
|
706
|
+
le = ::Logging::LogEvent.new(@logger, ::Logging::LEVELS['todo'], msg, true)
|
707
|
+
@logger.logEvent(le) unless @TODO[line]
|
708
|
+
@TODO[line] = true
|
709
|
+
end
|
710
|
+
end
|
711
|
+
|
712
|
+
# --------------------------------------------------------------------------------
|
713
|
+
def matches(string, criterium)
|
714
|
+
if criterium =~ %r/[\.\+\*\(\)\|\,\{\}\?\[\]\^\$]|\\[sSdDAzwWb]/
|
715
|
+
string.match(%r/#{criterium}/)
|
716
|
+
else
|
717
|
+
string == criterium
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
# --------------------------------------------------------------------------------
|
722
|
+
def execute(cmd,lead)
|
723
|
+
exit 1 if stop
|
724
|
+
print lead if @logger.level < 4
|
725
|
+
system cmd
|
726
|
+
end
|
727
|
+
|
728
|
+
# --------------------------------------------------------------------------------
|
729
|
+
def watch_for_break
|
730
|
+
Thread.new do
|
731
|
+
s=$stdin.read
|
732
|
+
#puts "Consumed existing input: '#{$stdin.read}'"
|
733
|
+
loop do
|
734
|
+
s = gets.chomp
|
735
|
+
if s != ""
|
736
|
+
puts "Interrupted! You entered '#{s}'"
|
737
|
+
@stop = true
|
738
|
+
exit
|
739
|
+
end
|
740
|
+
end
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
744
|
+
# --------------------------------------------------------------------------------
|
745
|
+
def callCmdProc(cmdp, a, b, c)
|
746
|
+
ret = nil
|
747
|
+
begin
|
748
|
+
if cmdp.is_a?(String)
|
749
|
+
ret = cmdp
|
750
|
+
elsif cmdp.is_a?(Proc)
|
751
|
+
ret = cmdp.call(a, b, c)
|
752
|
+
else
|
753
|
+
raise ChopInternalError.new("'#{cmdp}' is not a Proc, Lambda or String!")
|
754
|
+
end
|
755
|
+
rescue ChopInternalError => e
|
756
|
+
raise e
|
757
|
+
rescue => e
|
758
|
+
@logger.fatal "#{e.class.name} #{e.message}"
|
759
|
+
raise ChopError.new("#{e.class.name} #{e.message}")
|
760
|
+
end
|
761
|
+
ret
|
762
|
+
end
|
763
|
+
|
764
|
+
# --------------------------------------------------------------------------------
|
765
|
+
def databags(options=nil,exts=nil)
|
766
|
+
options = @config unless options
|
767
|
+
unless @databags
|
768
|
+
|
769
|
+
want = Hash.new
|
770
|
+
options[:databags].each{ |b|
|
771
|
+
match = b.match(%r/^(.*):(.*)$/)
|
772
|
+
if match
|
773
|
+
want[match[1]] = parseOptionString(match[2],';')
|
774
|
+
end
|
775
|
+
}
|
776
|
+
@logger.debug want.ai
|
777
|
+
|
778
|
+
chef = options[:repo_path]
|
779
|
+
raise ChopError.new "Oops! Where is the '#{chef}' directory? Also check cookbook path '#{options[:cookbook_path]}'" unless File.directory?(chef)
|
780
|
+
|
781
|
+
@databags={}
|
782
|
+
Dir.glob("#{chef}/data_bags/*").each{ |d|
|
783
|
+
if File.directory?(d)
|
784
|
+
name = File.basename(d)
|
785
|
+
regex = "^(#{want.keys.join('|')})"
|
786
|
+
match = matches(name,regex)
|
787
|
+
if match
|
788
|
+
@databags[name] = getPathSet(want[name], "data_bags/#{name}", exts)
|
789
|
+
@logger.info "Data bags list: #{@databags[name].values.map{|f| "#{name}/#{File.basename(f)}" }}"
|
790
|
+
end
|
791
|
+
end
|
792
|
+
}
|
793
|
+
end
|
794
|
+
@databags
|
795
|
+
end
|
796
|
+
|
797
|
+
# --------------------------------------------------------------------------------
|
798
|
+
def roles(options=nil,exts=nil)
|
799
|
+
options = @config unless options
|
800
|
+
unless @roles
|
801
|
+
@roles = getPathSet(options[:roles], 'roles', exts)
|
802
|
+
@logger.info "Roles list: #{@roles.values.map{|f| File.basename(f)}.ai}"
|
803
|
+
end
|
804
|
+
@roles
|
805
|
+
end
|
806
|
+
|
807
|
+
# --------------------------------------------------------------------------------
|
808
|
+
def environments(options=nil,exts=nil)
|
809
|
+
options = @config unless options
|
810
|
+
unless @environments
|
811
|
+
@environments = getPathSet(options[:envs], 'environments', exts)
|
812
|
+
@logger.info "Environments list: #{@environments.values.map{|f| File.basename(f)}.ai}"
|
813
|
+
end
|
814
|
+
@environments
|
815
|
+
end
|
816
|
+
|
817
|
+
end
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
|