luban 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +37 -0
  6. data/Rakefile +1 -0
  7. data/bin/console +14 -0
  8. data/bin/setup +7 -0
  9. data/exe/luban +3 -0
  10. data/lib/luban/deployment/cli/application/authenticator.rb +106 -0
  11. data/lib/luban/deployment/cli/application/base.rb +179 -0
  12. data/lib/luban/deployment/cli/application/builder.rb +67 -0
  13. data/lib/luban/deployment/cli/application/publisher.rb +215 -0
  14. data/lib/luban/deployment/cli/application/repository.rb +175 -0
  15. data/lib/luban/deployment/cli/application/scm/git.rb +49 -0
  16. data/lib/luban/deployment/cli/application/scm/rsync.rb +47 -0
  17. data/lib/luban/deployment/cli/application.rb +5 -0
  18. data/lib/luban/deployment/cli/command.rb +360 -0
  19. data/lib/luban/deployment/cli/package/binary.rb +241 -0
  20. data/lib/luban/deployment/cli/package/dependency.rb +49 -0
  21. data/lib/luban/deployment/cli/package/dependency_set.rb +71 -0
  22. data/lib/luban/deployment/cli/package/installer/core.rb +98 -0
  23. data/lib/luban/deployment/cli/package/installer/install.rb +330 -0
  24. data/lib/luban/deployment/cli/package/installer/paths.rb +81 -0
  25. data/lib/luban/deployment/cli/package/installer.rb +3 -0
  26. data/lib/luban/deployment/cli/package/service.rb +17 -0
  27. data/lib/luban/deployment/cli/package/worker.rb +43 -0
  28. data/lib/luban/deployment/cli/package.rb +6 -0
  29. data/lib/luban/deployment/cli/project.rb +94 -0
  30. data/lib/luban/deployment/cli.rb +4 -0
  31. data/lib/luban/deployment/configuration/core.rb +67 -0
  32. data/lib/luban/deployment/configuration/filter.rb +54 -0
  33. data/lib/luban/deployment/configuration/question.rb +38 -0
  34. data/lib/luban/deployment/configuration/server.rb +70 -0
  35. data/lib/luban/deployment/configuration/server_set.rb +86 -0
  36. data/lib/luban/deployment/configuration.rb +5 -0
  37. data/lib/luban/deployment/error.rb +5 -0
  38. data/lib/luban/deployment/helpers/configuration.rb +159 -0
  39. data/lib/luban/deployment/helpers/utils.rb +180 -0
  40. data/lib/luban/deployment/helpers.rb +2 -0
  41. data/lib/luban/deployment/packages/bundler.rb +81 -0
  42. data/lib/luban/deployment/packages/git.rb +37 -0
  43. data/lib/luban/deployment/packages/openssl.rb +59 -0
  44. data/lib/luban/deployment/packages/ruby.rb +125 -0
  45. data/lib/luban/deployment/packages/rubygems.rb +89 -0
  46. data/lib/luban/deployment/packages/yaml.rb +33 -0
  47. data/lib/luban/deployment/parameters.rb +160 -0
  48. data/lib/luban/deployment/runner.rb +99 -0
  49. data/lib/luban/deployment/templates/envrc.erb +30 -0
  50. data/lib/luban/deployment/templates/unset_envrc.erb +28 -0
  51. data/lib/luban/deployment/version.rb +5 -0
  52. data/lib/luban/deployment/worker/base.rb +71 -0
  53. data/lib/luban/deployment/worker/controller.rb +11 -0
  54. data/lib/luban/deployment/worker/local.rb +19 -0
  55. data/lib/luban/deployment/worker/remote.rb +55 -0
  56. data/lib/luban/deployment/worker/task.rb +25 -0
  57. data/lib/luban/deployment/worker.rb +4 -0
  58. data/lib/luban/deployment.rb +8 -0
  59. data/lib/luban.rb +4 -0
  60. data/luban.gemspec +29 -0
  61. metadata +174 -0
@@ -0,0 +1,330 @@
1
+ module Luban
2
+ module Deployment
3
+ module Package
4
+ class Installer
5
+ class InstallFailure < Luban::Deployment::Error; end
6
+
7
+ def required_packages
8
+ @required_packages ||=
9
+ self.class.package_class(package_name).
10
+ required_packages_for(package_major_version)
11
+ end
12
+
13
+ def configure_executable
14
+ @configure_executable ||= './configure'
15
+ end
16
+
17
+ def installed?
18
+ raise NotImplementedError, "#{self.class.name}#installed? is an abstract method."
19
+ end
20
+
21
+ def validate_download_url
22
+ info "Validating download URL for #{package_full_name}"
23
+ validate_download_url!
24
+ end
25
+
26
+ def install
27
+ info "Installing #{package_full_name}"
28
+ if installed?
29
+ if force?
30
+ install!
31
+ else
32
+ update_result "Skipped! #{package_full_name} has been installed ALREADY.",
33
+ status: :skipped
34
+ return
35
+ end
36
+ else
37
+ install!
38
+ end
39
+
40
+ if installed?
41
+ update_result "Successfully installed #{package_full_name}."
42
+ else
43
+ update_result "Failed to install #{package_full_name}. " +
44
+ "Please check install log for details: #{install_log_file_path}",
45
+ status: :failed, level: :error
46
+ end
47
+ end
48
+
49
+ def uninstall
50
+ info "Uninstalling #{package_full_name}"
51
+ if installed?
52
+ if current? and !force?
53
+ update_result "Skippped! #{package_full_name} is the current version in use. " +
54
+ "Please switch to other version before uninstalling this version or " +
55
+ "use -f to force uninstalling.",
56
+ status: :skipped, level: :warn
57
+ else
58
+ uninstall!
59
+ update_result "#{package_full_name} is uninstalled. "
60
+ end
61
+ else
62
+ message = "#{package_full_name} is NOT installed.",
63
+ if directory?(install_path) or directory?(build_path)
64
+ if force?
65
+ uninstall!
66
+ update_result message +
67
+ "Cleaned up leftover of #{package_full_name} from last installation."
68
+ else
69
+ update_result message +
70
+ "But leftover from last installation is found. " +
71
+ "Use -f to force clean it up."
72
+ end
73
+ else
74
+ update_result "Skipped! #{message}"
75
+ end
76
+ end
77
+ end
78
+
79
+ def cleanup_all
80
+ cleanup_temp!
81
+ cleanup_logs!
82
+ update_result "Cleaned up temporary files in #{package_full_name} installation"
83
+ end
84
+
85
+ def get_summary
86
+ status = if current_symlinked?
87
+ current? ? " *" : "s*"
88
+ else
89
+ current? ? "c*" : " "
90
+ end
91
+
92
+ if installed?
93
+ installed = '(installed)'
94
+ alert = case status
95
+ when "s*"
96
+ "Alert! #{package_full_name} is not the current version but symlinked IMPROPERLY. " +
97
+ "Run \"binstubs\" to fix it."
98
+ when "c*"
99
+ "Alert! #{package_full_name} is set as current version but NOT symlinked properly. " +
100
+ "Run \"binstubs\" to fix it."
101
+ end
102
+ else
103
+ installed = '(NOT installed)'
104
+ alert = nil
105
+ end
106
+ update_result summary: { name: package_full_name, installed: installed,
107
+ status: status, alert: alert }
108
+ end
109
+
110
+ def update_binstubs
111
+ if current?
112
+ if installed?
113
+ update_binstubs!
114
+ update_result "Updated #{package_name} binstubs with current version #{package_version}"
115
+ else
116
+ update_result "Skipped! #{package_full_name} is NOT installed yet. Please install it first.",
117
+ status: :failed, level: :error
118
+ end
119
+ else
120
+ if current_symlinked?
121
+ remove_binstubs!
122
+ remove_symlinks!
123
+ update_result "Removed #{package_name} binstubs with version #{package_version}. " +
124
+ "Current version of #{package_name} is NOT specified.",
125
+ level: :warn
126
+ end
127
+ end
128
+ end
129
+
130
+ def which_current
131
+ get_summary
132
+ task.result.summary[:executable] = "Not found"
133
+ if current? and current_symlinked? and
134
+ file?(executable = File.join(readlink(current_path), 'bin', task.args.executable))
135
+ task.result.summary[:executable] = executable
136
+ end
137
+ end
138
+
139
+ def whence_origin
140
+ get_summary
141
+ task.result.summary[:executable] = "Not found"
142
+ if file?(executable = bin_path.join(task.args.executable))
143
+ task.result.summary[:executable] = executable
144
+ end
145
+ end
146
+
147
+ protected
148
+
149
+ def before_install
150
+ bootstrap
151
+ unless installed? or file?(src_file_path)
152
+ validate_download_url
153
+ end
154
+ install_required_packages(:before_install)
155
+ end
156
+
157
+ def after_install
158
+ install_required_packages(:after_install)
159
+ update_binstubs!
160
+ end
161
+
162
+ def install_required_packages(type)
163
+ required_packages[type].each do |d|
164
+ self.class.worker_class(:installer, package: d.name).new(
165
+ config: config, backend: backend,
166
+ cmd: :install, args: {},
167
+ opts: d.options.merge(name: d.name, version: d.version, parent: self)
168
+ ).run
169
+ end
170
+ end
171
+
172
+ def validate_download_url!
173
+ unless url_exists?(download_url)
174
+ raise InstallFailure,
175
+ "Package #{package_full_name} is NOT found from url: #{download_url}."
176
+ end
177
+ end
178
+
179
+ def install!
180
+ download_package
181
+ uncompress_package
182
+ assure_dirs(build_path)
183
+ within build_path do
184
+ with compose_build_env_variables do
185
+ build_package
186
+ end
187
+ end
188
+ cleanup_build!
189
+ end
190
+
191
+ def uninstall!
192
+ if current?
193
+ remove_binstubs!
194
+ remove_symlinks!
195
+ end
196
+ rmdir(install_path)
197
+ cleanup_temp!
198
+ cleanup_logs!
199
+ end
200
+
201
+ def cleanup_temp!
202
+ Luban::Deployment::Package::DependencyTypes.each do |type|
203
+ required_packages[type].each do |d|
204
+ self.class.worker_class(:installer, package: d.name).new(
205
+ config: config, backend: backend,
206
+ cmd: :cleanup_all, args: {},
207
+ opts: d.options.merge(name: d.name, version: d.version, parent: self)
208
+ ).run
209
+ end
210
+ end
211
+ cleanup_build!
212
+ end
213
+
214
+ def cleanup_build!
215
+ rmdir(build_path)
216
+ end
217
+
218
+ def cleanup_logs!
219
+ rmdir(install_log_path)
220
+ end
221
+
222
+ def update_binstubs!
223
+ remove_binstubs!
224
+ remove_symlinks!
225
+ create_symlinks!
226
+ create_binstubs!
227
+ end
228
+
229
+ def create_symlinks!
230
+ ln(install_path, current_path)
231
+ end
232
+
233
+ def create_binstubs!
234
+ return unless directory?(bin_path)
235
+ find_cmd = "find #{bin_path}/* -type f -print && find #{bin_path}/* -type l -print"
236
+ capture(find_cmd).split("\n").each do |bin|
237
+ ln(bin, app_bin_path.join(File.basename(bin)))
238
+ end
239
+ end
240
+
241
+ def remove_symlinks!
242
+ return unless symlink?(current_path)
243
+ execute "[ -h #{current_path} ] && rm -f #{current_path}; true"
244
+ end
245
+
246
+ def remove_binstubs!
247
+ return unless directory?(current_bin_path)
248
+ find_cmd = "find #{current_bin_path}/* -type f -print && find #{current_bin_path}/* -type l -print"
249
+ capture(find_cmd).split("\n").each do |bin|
250
+ bin_symlink = app_bin_path.join(File.basename(bin))
251
+ execute "[ -h #{bin_symlink} ] && rm -f #{bin_symlink}; true"
252
+ end
253
+ end
254
+
255
+ def bootstrap
256
+ assure_dirs(etc_path, tmp_path, app_bin_path, install_path, install_log_path)
257
+ end
258
+
259
+ def download_package
260
+ info "Downloading #{package_full_name} source package"
261
+ if file?(src_file_path)
262
+ info "#{package_full_name} is downloaded ALREADY"
263
+ else
264
+ download_package!
265
+ end
266
+ end
267
+
268
+ def download_package!
269
+ unless test("curl -L -o #{src_file_path} #{download_url} >> #{install_log_file_path} 2>&1")
270
+ rm(src_file_path)
271
+ abort_action('download')
272
+ end
273
+ end
274
+
275
+ def uncompress_package
276
+ info "Uncompressing #{package_full_name} source package"
277
+ uncompress_package!
278
+ end
279
+
280
+ def uncompress_package!
281
+ unless test("tar -xzf #{src_file_path} -C #{package_tmp_path} >> #{install_log_file_path} 2>&1")
282
+ abort_action('uncompress')
283
+ end
284
+ end
285
+
286
+ def build_package
287
+ configure_package
288
+ make_package
289
+ install_package
290
+ end
291
+
292
+ def configure_package
293
+ info "Configuring #{package_full_name}"
294
+ abort_action('configure') unless configure_package!
295
+ end
296
+
297
+ def configure_package!
298
+ test(configure_executable,
299
+ "#{compose_build_options} >> #{install_log_file_path} 2>&1")
300
+ end
301
+
302
+ def make_package
303
+ info "Making #{package_full_name}"
304
+ abort_action('make') unless make_package!
305
+ end
306
+
307
+ def make_package!
308
+ test(:make, ">> #{install_log_file_path} 2>&1")
309
+ end
310
+
311
+ def install_package
312
+ info "Installing #{package_full_name}"
313
+ abort_action('install') unless install_package!
314
+ end
315
+
316
+ def install_package!
317
+ test(:make, "install >> #{install_log_file_path} 2>&1")
318
+ end
319
+
320
+ def abort_action(action)
321
+ cleanup_temp!
322
+ task.result.status = :failed
323
+ task.result.message = "Failed to #{action} package #{package_full_name}." +
324
+ "Please check install log for details: #{install_log_file_path}"
325
+ raise InstallFailure, task.result.message
326
+ end
327
+ end
328
+ end
329
+ end
330
+ end
@@ -0,0 +1,81 @@
1
+ module Luban
2
+ module Deployment
3
+ module Package
4
+ class Installer
5
+ DefaultSrcFileExtName = 'tar.gz'
6
+
7
+ def src_file_name
8
+ @src_file_name ||= "#{package_full_name}.#{src_file_extname}"
9
+ end
10
+
11
+ def src_file_extname
12
+ @src_file_extname ||= DefaultSrcFileExtName
13
+ end
14
+
15
+ def src_file_path
16
+ @src_file_path ||= tmp_path.join(src_file_name)
17
+ end
18
+
19
+ def source_repo
20
+ raise NotImplementedError, "#{self.class.name}#source_repo is an abstract method."
21
+ end
22
+
23
+ def download_from_source?
24
+ lubhub_repo.nil?
25
+ end
26
+
27
+ def download_from_lubhub?
28
+ !lubhub_repo.nil?
29
+ end
30
+
31
+ def download_repo
32
+ @download_repo ||= download_from_source? ? source_repo : lubhub_repo
33
+ end
34
+
35
+ def source_url_root
36
+ raise NotImplementedError, "#{self.class.name}#source_url_root is an abstract method."
37
+ end
38
+
39
+ def download_url_root
40
+ @download_url_root ||= download_from_source? ? source_url_root : package_name
41
+ end
42
+
43
+ def download_url
44
+ @download_url ||= File.join(download_repo, download_url_root, src_file_name)
45
+ end
46
+
47
+ def install_path
48
+ @install_path ||= package_path.join('versions', package_version)
49
+ end
50
+
51
+ def bin_path
52
+ @bin_path ||= install_path.join('bin')
53
+ end
54
+
55
+ def lib_path
56
+ @lib_path ||= install_path.join('lib')
57
+ end
58
+
59
+ def include_path
60
+ @include_path ||= install_path.join('include')
61
+ end
62
+
63
+ def build_path
64
+ @build_path ||= package_tmp_path.join(package_full_name)
65
+ end
66
+
67
+ def install_log_path
68
+ @install_log_path ||= package_path.join('log').join(package_full_name)
69
+ end
70
+
71
+ def install_log_file_path
72
+ @install_log_file_path ||= install_log_path.join(install_log_file_name)
73
+ end
74
+
75
+ def install_log_file_name
76
+ @install_log_file_name ||= "#{package_full_name}-install-#{Time.now.strftime("%Y%m%d-%H%M%S")}.log"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'installer/core'
2
+ require_relative 'installer/paths'
3
+ require_relative 'installer/install'
@@ -0,0 +1,17 @@
1
+ module Luban
2
+ module Deployment
3
+ module Package
4
+ class Service < Binary
5
+ include Luban::Deployment::Command::Tasks::Deploy
6
+ include Luban::Deployment::Command::Tasks::Control
7
+
8
+ %i(deploy).each do |m|
9
+ define_task_method(m, worker: :deployer)
10
+ end
11
+
12
+ protected
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ module Luban
2
+ module Deployment
3
+ module Package
4
+ class Worker < Luban::Deployment::Worker::Remote
5
+ class << self
6
+ def package_class(package)
7
+ Luban::Deployment::Package::Base.package_class(package)
8
+ end
9
+
10
+ def worker_class(worker, **opts)
11
+ Luban::Deployment::Package::Base.worker_class(worker, **opts)
12
+ end
13
+ end
14
+
15
+ def package_name; task.opts.name; end
16
+ def package_full_name; "#{package_name}-#{package_version}"; end
17
+
18
+ def package_version; task.opts.version; end
19
+ def package_major_version; task.opts.major_version; end
20
+ def package_patch_level; task.opts.patch_level; end
21
+
22
+ def child?; !task.opts.parent.nil?; end
23
+ def parent; task.opts.parent; end
24
+
25
+ def current_path
26
+ @current_path ||= app_path.join(package_name)
27
+ end
28
+
29
+ def current_bin_path
30
+ @current_bin_path ||= current_path.join('bin')
31
+ end
32
+
33
+ def package_path
34
+ @package_path ||= luban_install_path.join('pkg', package_name)
35
+ end
36
+
37
+ def package_tmp_path
38
+ @package_tmp_path ||= package_path.join('tmp')
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,6 @@
1
+ require_relative 'package/dependency'
2
+ require_relative 'package/dependency_set'
3
+ require_relative 'package/binary'
4
+ require_relative 'package/service'
5
+ require_relative 'package/worker'
6
+ require_relative 'package/installer'
@@ -0,0 +1,94 @@
1
+ module Luban
2
+ module Deployment
3
+ class Project < Luban::Deployment::Command
4
+ using Luban::CLI::CoreRefinements
5
+ include Luban::Deployment::Parameters::Project
6
+ include Luban::Deployment::Command::Tasks::Install
7
+ include Luban::Deployment::Command::Tasks::Deploy
8
+ include Luban::Deployment::Command::Tasks::Control
9
+
10
+ attr_reader :apps
11
+
12
+ def display_name; @display_name ||= "#{stage} #{project.camelcase}"; end
13
+
14
+ def package_users_for(package_name, package_version, exclude: [], servers: [])
15
+ apps.values.inject([]) do |package_users, app|
16
+ if !exclude.include?(app.name) and
17
+ app.use_package?(package_name, package_version, servers: servers)
18
+ package_users << app.display_name
19
+ end
20
+ package_users
21
+ end
22
+ end
23
+
24
+ def password_for(user)
25
+ @passwords_mutex.synchronize do
26
+ if @passwords[user].nil?
27
+ @passwords[user] = ask(prompt: "Password for #{user}", echo: false)
28
+ end
29
+ @passwords[user]
30
+ end
31
+ end
32
+
33
+ %i(installable? deployable? controllable?).each do |method|
34
+ define_method(method) do
35
+ apps.values.any? { |app| app.send(__method__) }
36
+ end
37
+ end
38
+
39
+ %i(build destroy_project cleanup binstubs
40
+ show_current show_summary which whence
41
+ ).each do |action|
42
+ define_method(action) do |args:, opts:|
43
+ apps.each_value do |app|
44
+ app.send(__method__, args: args, opts: opts) if app.installable?
45
+ end
46
+ end
47
+ end
48
+
49
+ alias_method :destroy, :destroy_project
50
+
51
+ protected
52
+
53
+ def validate_parameters
54
+ super
55
+ validate_project_parameters
56
+ end
57
+
58
+ def set_default_parameters
59
+ super
60
+ set_default :stage, self.class.name.split('::').first.snakecase
61
+ set_default :project, self.class.name.split('::').last.snakecase
62
+ set_default_project_parameters
63
+ @passwords = {}
64
+ @passwords_mutex = Mutex.new
65
+ end
66
+
67
+ def load_libraries
68
+ applications.each do |app|
69
+ require "#{work_dir}/apps/#{app}/lib/application"
70
+ end
71
+ end
72
+
73
+ def application_base_class(app)
74
+ Object.const_get("#{project}:#{app}".camelcase)
75
+ end
76
+
77
+ def setup_cli
78
+ setup_applications
79
+ super
80
+ end
81
+
82
+ def setup_descriptions
83
+ desc "Manage apps in #{display_name}"
84
+ end
85
+
86
+ def setup_applications
87
+ @apps = {}
88
+ applications.map(&:to_sym).each do |app|
89
+ @apps[app] = command(app, base: application_base_class(app))
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,4 @@
1
+ require_relative 'cli/command'
2
+ require_relative 'cli/project'
3
+ require_relative 'cli/application'
4
+ require_relative 'cli/package'
@@ -0,0 +1,67 @@
1
+ module Luban
2
+ module Deployment
3
+ class Configuration
4
+ attr_reader :variables
5
+ attr_reader :servers
6
+
7
+ def initialize
8
+ @variables = {}
9
+ @servers = ServerSet.new
10
+ end
11
+
12
+ def set(key, value)
13
+ @variables[key] = value
14
+ end
15
+
16
+ def set_default(key, value)
17
+ set(key, value) unless @variables.has_key?(key)
18
+ end
19
+
20
+ def delete(key)
21
+ @variables[key] = value
22
+ end
23
+
24
+ def fetch(key, default = nil, &blk)
25
+ value = if block_given?
26
+ @variables.fetch(key, &blk)
27
+ else
28
+ @variables.fetch(key, default)
29
+ end
30
+ while callable_without_parameters?(value)
31
+ value = set(key, value.call)
32
+ end
33
+ return value
34
+ end
35
+
36
+ def keys
37
+ @variables.keys
38
+ end
39
+
40
+ def role(name, hosts, **properties)
41
+ if name == :all
42
+ raise ArgumentError, 'Reserved role name, :all, is NOT allowed to use.'
43
+ end
44
+ @servers.add_hosts_for_role(name, hosts, properties)
45
+ end
46
+
47
+ def server(name, **properties)
48
+ new_server = servers.add_host(name, properties)
49
+ if new_server
50
+ new_server.ssh_options = fetch(:ssh_options) || {}
51
+ end
52
+ end
53
+
54
+ def ask(key=nil, default:, prompt: nil, echo: true)
55
+ Question.new(default: default, prompt: prompt, echo: echo).call.tap do |answer|
56
+ set(key, answer) unless key.nil?
57
+ end
58
+ end
59
+
60
+ protected
61
+
62
+ def callable_without_parameters?(x)
63
+ x.respond_to?(:call) && (!x.respond_to?(:arity) || x.arity == 0)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,54 @@
1
+ module Luban
2
+ module Deployment
3
+ class Configuration
4
+ def roles(*names)
5
+ opts = names.last.is_a?(::Hash) ? names.pop : {}
6
+ filter_servers(servers.find_by_roles(filter_roles(names), opts))
7
+ end
8
+
9
+ def release_roles(*names)
10
+ if names.last.is_a?(Hash)
11
+ names.last.merge(:exclude => :no_release)
12
+ else
13
+ names << { :exclude => :no_release }
14
+ end
15
+ roles(*names)
16
+ end
17
+
18
+ def primary(role)
19
+ servers.find_primary(role)
20
+ end
21
+
22
+ protected
23
+
24
+ def filter_roles(_roles)
25
+ available_roles = (env_filter(:roles) | config_filter(:roles)).map(&:to_sym)
26
+ available_roles = servers.available_roles if available_roles.empty?
27
+ if _roles.include?(:all)
28
+ available_roles
29
+ else
30
+ _roles.select { |name| available_roles.include?(name) }
31
+ end
32
+ end
33
+
34
+ def filter_servers(_servers)
35
+ filter_hosts = env_filter(:hosts) | config_filter(:hosts)
36
+ if filter_hosts.empty?
37
+ _servers
38
+ else
39
+ _servers.select { |server| filter_hosts.include?(server.hostname) }
40
+ end
41
+ end
42
+
43
+ def env_filter(type)
44
+ type = type.to_s.upcase
45
+ ENV[type].nil? ? [] : ENV[type].split(',')
46
+ end
47
+
48
+ def config_filter(type)
49
+ filter = fetch(:filter) || fetch(:select)
50
+ filter.nil? ? [] : filter.fetch(type, [])
51
+ end
52
+ end
53
+ end
54
+ end