forj 1.0.10 → 1.0.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 104016c81c311f9a0f4454a0f50aadeb9e2810a2
4
- data.tar.gz: 07452ff472445eed9d882d1d8e153c75b7b27182
3
+ metadata.gz: 5c59dea68c4b93b5a88af774d515d0d5c6aece5a
4
+ data.tar.gz: ba80c68baec1fada231973d365f7bc3d46cb6306
5
5
  SHA512:
6
- metadata.gz: dc95fd28f3de676003c62a1804725ca4225199391c78096a200a048c92d02207324d19ffd6a127b9a446baafe6b16a28ca3a49c90d676e2a6db33fbf35dd75a0
7
- data.tar.gz: f1f431e9d6ebb546d1f524b921db1098ce537ec5a263ea310a87f2b6ff36fc35261d37552f8b4e0fe8568d551ee5beb9daf115b8042f689a4ca26ea0c508688f
6
+ metadata.gz: d8f0791f66cb366aef89a6845e0a8ac13869e08e3e27e659824513b0056b27950503f2c354ddf4d5b00798ea60c34e3a0d94d2e56a6da992dc6bb290319f5e12
7
+ data.tar.gz: de46007515850844f71c4dbc2d7375991c58c2be4c9118b39b432694ecb7939a9c552945fc3a5cb19d830f953c77158f7f5096228e534828647418b65e45e9cb
data/.rubocop.yml CHANGED
@@ -33,27 +33,24 @@ Style/HashSyntax:
33
33
 
34
34
  # lets start with 40, but 10 is way to small..
35
35
  Metrics/MethodLength:
36
- Max: 40
36
+ Max: 50
37
+ # If Method length is increased, class length need to be extended as well.
38
+ Metrics/ClassLength:
39
+ Max: 150
40
+
37
41
  # allow arguments to be longer than 15
38
42
  Metrics/AbcSize:
39
- Max: 40
40
- # allow nested methods and classes definition
41
- ClassAndModuleChildren:
42
- # EnforcedStyle: compact
43
- Enabled: false
43
+ Max: 50
44
+ # Perceived Complexity
45
+ Metrics/PerceivedComplexity:
46
+ Max: 9
47
+ Metrics/CyclomaticComplexity:
48
+ Max: 8
44
49
 
45
- # some files names gets an exception
50
+ # forj-docker binary name gets an exception
46
51
  Style/FileName:
47
52
  Exclude: ['Gemfile',
48
53
  'lib/forj/process/ForjProcess.rb',
49
54
  'lib/forj/ForjCore.rb',
50
55
  'lib/forj/ForjCli.rb',
51
56
  'lib/forj-settings.rb' ]
52
-
53
- # this project defines these globals
54
- Style/GlobalVars:
55
- AllowedVariables: ['$RT_GEM_BIN',
56
- '$RT_VERSION_SPEC',
57
- '$FORJ_TEMP',
58
- '$RT_VERSION',
59
- '$RT_GEM_HOME']
data/bin/forj CHANGED
@@ -21,6 +21,7 @@ require 'rubygems'
21
21
  require 'bundler/setup'
22
22
  require 'thor'
23
23
  require 'ansi'
24
+ require 'forj'
24
25
 
25
26
  APP_PATH = File.dirname(File.dirname(__FILE__))
26
27
  LIB_PATH = File.expand_path(File.join(APP_PATH, 'lib'))
@@ -148,21 +149,27 @@ If you want to check/updated them, use `forj get [-a account]`
148
149
  ' flavor : Maestro flavor to use.'
149
150
  method_option :bp_flavor, :aliases => '-b', :desc => 'config:' \
150
151
  ' bp_flavor : Blueprint nodes default flavor to use.'\
151
- "\n\n Build system options:"
152
+ "\n\nBuild system options:"
152
153
  method_option :boothook, :aliases => '-H', :desc => 'By default, ' \
153
154
  ' boothook file used is build/bin/build-tools/boothook.sh. ' \
154
155
  ' Use this option to set another one.'
155
156
  method_option :build, :aliases => '-B', :desc => 'Replace' \
156
157
  ' the default build.sh'
157
158
  method_option :branch, :aliases => '-R', :desc => 'Branch' \
158
- "name to clone for maestro.\n\nMaestro/infra bootstrap debugging:"
159
- method_option :test_box, :aliases => '-T', :desc => 'Identify a' \
160
- ' path to become your test-box repository.' \
161
- "\n " \
162
- 'Ex: if your maestro is in ~/src/forj-oss, --test_box' \
163
- ' ~/src/forj-oss/maestro build.sh and' \
164
- "\n " \
165
- 'test-box will send your local maestro repo to your box, for boot.'
159
+ "name to clone for maestro.\n\n"\
160
+ "Remote box bootstrap debugging (test-box):\n"\
161
+ 'test-box is a shell script used to connect one or more local repository'\
162
+ " to be connected to the new remote box. \n"\
163
+ 'If your box support test-box feature, at boot time, the remote box may '\
164
+ 'wait for your local repository to be sent out to the new box. '\
165
+ 'For more details on test-box, call it to get help.'
166
+ method_option :tb_path, :aliases => '-t',
167
+ :desc => 'Define the path to the test-box script. '\
168
+ 'This option superseeds the TEST_BOX '\
169
+ 'environment variable.'
170
+ method_option :test_box, :aliases => '-T', :type => :array,
171
+ :desc => "Use a local repository for test-box\n\n"\
172
+ 'Other options:'
166
173
  method_option :extra_metadata, :aliases => '-e', :desc => 'Custom' \
167
174
  ' server metadata format key1=value1,key2=value2...,keyN=valueN'
168
175
 
data/forj.gemspec CHANGED
@@ -19,8 +19,8 @@ Gem::Specification.new do |s|
19
19
  s.name = 'forj'
20
20
  s.homepage = 'https://www.forj.io'
21
21
 
22
- s.version = '1.0.10'
23
- s.date = '2015-04-17'
22
+ s.version = '1.0.11'
23
+ s.date = '2015-05-22'
24
24
  s.summary = 'forj command line'
25
25
  s.description = 'forj cli - See https://www.forj.io for documentation/information'
26
26
 
@@ -51,7 +51,7 @@ Gem::Specification.new do |s|
51
51
  s.add_runtime_dependency 'json', '1.7.5'
52
52
  s.add_runtime_dependency 'bundler'
53
53
  s.add_runtime_dependency 'nokogiri','1.5.11'
54
- s.add_runtime_dependency 'lorj_cloud', '~> 0.1.0'
54
+ s.add_runtime_dependency 'lorj_cloud', '~> 0.1.1'
55
55
 
56
56
  s.add_development_dependency "rake", "~> 10.0"
57
57
  s.add_development_dependency "rspec", "~> 3.1.0"
data/forj/defaults.yaml CHANGED
@@ -13,285 +13,4 @@
13
13
  # limitations under the License.
14
14
 
15
15
  :default:
16
- :maestro_url: https://github.com/forj-oss/maestro.git
17
-
18
- # Default Infra repository to use. If missing, it will be proposed to be created.
19
- :infra_repo: ~/.forj/infra
20
-
21
- # You can set proto2b in your ~/.forj/config.yaml if you built it from maestro/build. Read the maestro/README.md to create it.
22
- :image_name: Ubuntu Precise 12.04.4 LTS Server 64-bit 20140414 (Rescue Image)
23
-
24
- # Flavor to use for Maestro
25
- :flavor: medium
26
- # Default flavor to use for all Blueprint nodes.
27
- :bp_flavor: small
28
-
29
- # Ports to open for Maestro, added to the security group
30
- :security_group: forj
31
- :ports: [22, 80, 443, 3000, 3131-3135, 4505-4506, 5000, 5666, 8000, 8080-8081, 8083, 8125, 8139-8140, 8773-8776, 9292, 29418, 35357]
32
-
33
- # Network: If network doesn't exist, forj cli will try to create it, and attach it a router.
34
- :network: forj
35
-
36
- # Users: Default user for ssh connection, if user doesn't exits, forj cli will try to get the user from the server image on it's name attribute
37
- :users: ['ubuntu', 'fedora', 'cloud-user', 'cirros', 'centos', 'cloud', 'root']
38
-
39
- # build.sh internal variables.
40
- :build_config: box
41
- :branch: master
42
- :box_name: maestro
43
-
44
16
  :provider_name: hpcloud
45
-
46
- :maestro_bootstrap_dir: build/maestro/bootstrap
47
- :description:
48
- # Description of build.sh environment variable defined by forj cli for build.sh. (~/.forj/infra/build/<Account>.build.env)
49
- :FORJ_HPC: "HPCloud cli Account used to build your Maestro box"
50
- :FORJ_HPC_COMPUTE: "HPCloud Compute service (like region-b.geo-1) used to run your Maestro Box"
51
- :FORJ_TENANT_NAME: "HPCloud Tenant name used build your <Blueprint> nodes"
52
- :FORJ_HPC_NET: "HPCloud Network name to use, while booting all boxes."
53
- :FORJ_KEYPAIR: "Keypair used to access boxes."
54
- :FORJ_SECURITY_GROUP: "Security group associated to each box"
55
- :FORJ_HPC_NOVA_KEYPUB: "Public key used by build.sh to ensure his existence on HPCloud"
56
- :FORJ_BASE_IMG: "Base image used to build all boxes"
57
- :FORJ_FLAVOR: "Flavor used to build Maestro"
58
- # DNS specific data
59
- :FORJ_DNS_TENANTID: "HPCloud Project ID to use to create DNS entries for each boxes."
60
- :FORJ_DNS_ZONE: "HPCloud Domain name service to use for each boxes DNS entries. (Ex: region-a.geo-1)"
61
- :FORJ_DNS_DOMAIN: "Domain used for DNS. Each server will be attached to a public IP. An 'A' record in the DNS service will need to be added to your HPCloud DOMAIN."
62
-
63
- # Following declares generic FORJ data to handle Fog object (compute/network/dns/...)
64
- # It defines the account file structure. section/key=value
65
- # All data can be predefined by default value (config.yaml/defaults.yaml) except
66
- # those identified by :account_exclusive: true
67
- :setup:
68
- :ask_step:
69
- - :desc: "Provider configuration:"
70
- - :desc: "Maestro Cloud compute configuration:"
71
- :explanation: |-
72
- Maestro (gardener) is currently configured to access your cloud Compute service with fog openstack.
73
- Fog openstack is compatible with hpcloud services
74
-
75
- It requires the openstack project name, user and password.
76
-
77
- - :desc: "Maestro and blueprint configuration:"
78
- :add:
79
- - :keypair_files
80
- - :ssh_user
81
- - :desc: "DNS Configuration for Maestro:"
82
- :sections:
83
- # This section define updatable data available from config.yaml. But will never be added in an account file.
84
- # Used by forj set/get functions
85
- :default:
86
- :account_name:
87
- :desc: "Default account name used by forj cli"
88
- :provider_name:
89
- :desc: "Default provider name while running forj setup. By default, hpcloud is selected."
90
- # Defines account credentials data
91
- :account:
92
- :name:
93
- :desc: "Name of the Forj cli account. use forj account rename <oldName> <NewName> to update it."
94
- :readonly: true
95
- :account_exclusive: true
96
- :provider:
97
- :desc: "Provider name attached to the forj cli account. To update it, use forj setup."
98
- :readonly: true
99
- :account_exclusive: true
100
- :default: :provider_name
101
-
102
- # Defines services
103
- :services:
104
- :compute:
105
- :desc: "Generic service identification for compute"
106
- :account_exclusive: true
107
- :account: true
108
- :ask_step: 0
109
- :network:
110
- :desc: "Generic service identification for network"
111
- :account_exclusive: true
112
- :account: true
113
- :ask_step: 0
114
-
115
- # Defines ssh keys credentials
116
- :credentials:
117
- :keypair_files:
118
- :explanation: |-
119
- A keypair is a combination of SSH public and private key files. Usually, generated in your '$HOME/.ssh/' directory.
120
- The private key is used to identify yourself to access your box via ssh.
121
- The public key is used to configure your server to authorize you to access the box with your private key.
122
- This keypair files will be copied to '$HOME/.forj/keypairs/ under <keypair_name> files for 'forj' needs.
123
-
124
- If the keypair doesn't exist locally, it will be created for you.
125
- :desc: "Base keypair file name"
126
- :default_value: "~/.ssh/<%= config[:keypair_name] %>-id_rsa"
127
- :validate_function: :forj_check_keypairs_files
128
- :account: true
129
- :ask_step: 2
130
- :after: :keypair_name
131
- :pre_step_function: :forj_cloud_keypair_coherent?
132
- :post_step_function: :forj_setup_keypairs_files
133
- :keypair_path:
134
- :desc: "Contains the full path to the :keypair_base."
135
- :default_value: "<%= Forj.keypairs_path %>"
136
- :keypair_base:
137
- :desc: "Contains the key file base name without .pem/.pub."
138
- :default_value: "<%= config[:keypair_name] %>"
139
- :keypair_name:
140
- :desc: "keypair name defined in your cloud to access your server. By default we named it 'forj'. If it doesn't exist, it will be created."
141
- :required: true
142
- :default_value: "forj"
143
- :account: true
144
- :ask_step: 2
145
- :pre_step_function: :update_keypair_config
146
- :post_step_function: :forj_check_cloud_keypair
147
- :auth_uri:
148
- :desc: "Generic service auth url"
149
- :account_exclusive: true
150
- :account: true
151
- :required: true
152
- :ask_sort: 0
153
- :account_id:
154
- :desc: "Generic Cloud Account name."
155
- :account_exclusive: true
156
- :account: true
157
- :required: true
158
- :account_key:
159
- :desc: "Generic cloud account key"
160
- :account_exclusive: true
161
- :account: true
162
- :required: true
163
- :encrypted: true
164
- :tenant:
165
- :desc: "Openstack Tenant Name (Project name)"
166
- :account_exclusive: true
167
- :account: true
168
- :required: true
169
- :ask_step: 0
170
- :os_user:
171
- :desc: "Openstack compute cloud User name"
172
- :account_exclusive: true
173
- :account: true
174
- :required: true
175
- :validate: !ruby/regexp /\w+/
176
- :ask_step: 1
177
- :default_value: "<%= (config[:provider_name] == 'openstack')?config[:account_id]:nil %>"
178
- :os_enckey:
179
- :desc: "Openstack compute cloud password"
180
- :account_exclusive: true
181
- :encrypted: true
182
- :account: true
183
- :required: true
184
- :ask_step: 1
185
- :default_value: "<%= (config[:provider_name] == 'openstack')?config[:account_key]:nil %>"
186
-
187
- # Defines DNS services for maestro
188
- :dns:
189
- :domain_name:
190
- :desc: "Domain name added to each hosts."
191
- :account_exclusive: true
192
- :account: true
193
- :post_step_function: :forj_dns_settings
194
- :ask_step: 3
195
- :dns_service:
196
- :desc: "DNS service region name Maestro will use."
197
- :account_exclusive: true
198
- :account: true
199
- :pre_step_function: :forj_dns_settings?
200
- :ask_step: 3
201
- :dns_tenant_id:
202
- :desc: "DNS Tenant ID Maestro will use"
203
- :account_exclusive: true
204
- :account: true
205
- :pre_step_function: :forj_dns_settings?
206
- :ask_step: 3
207
-
208
- :maestro:
209
- :tenant_name:
210
- :desc: "Tenant name required by fog/openstack on gardener"
211
- :account: true
212
- :validate: !ruby/regexp /^\w?[\w_:-]*$/
213
- :ask_step: 1
214
- :ask_sort: 0
215
- :default_value: "<%= (config[:provider_name] == 'openstack')?config[:tenant]:nil %>"
216
- :network_name:
217
- :desc: "Network name to attach to each forge boxes. By default we use 'forj'. If it doesn't exist, it will be created."
218
- :default: network
219
- :account: true
220
- :required: true
221
- :default_value: "forj"
222
- :ask_step: 2
223
- :security_group:
224
- :desc: "Security group name to configure and attach to each forge boxes."
225
- :account: true
226
- :validate: !ruby/regexp /^\w?\w*$/
227
- :default_value: "forj"
228
- :ask_step: 2
229
- :maestro_repo:
230
- :desc: "To use a different Maestro repository already cloned."
231
- :infra_repo:
232
- :desc: "Defines your Infra directory to use while booting."
233
- :box_name:
234
- :desc: "forj cli use 'build.sh' to create Maestro. See box_name option on build.sh to get more information. By default 'maestro'"
235
- :build_config:
236
- :desc: "forj cli use 'build.sh' to create Maestro. See build_config option on build.sh to get more information. By default 'box'"
237
- :bp_flavor:
238
- :desc: "Blueprint nodes default flavor"
239
- :explanation: |-
240
- Blueprint usually defines the required flavor for their nodes. If not, it will use this flavor as default.
241
- Usually, blueprint nodes are smaller than Maestro.
242
- :account: true
243
- :list_values:
244
- :query_type: :query_call # Will execute a query on flavor, query_params is empty for all.
245
- :object: :flavor
246
- :value: :name
247
- :validate: :list_strict
248
- :ask_step: 2
249
- :flavor_name:
250
- :explanation: 'This flavor is for Maestro only.'
251
- :desc: "Maestro Flavor name"
252
- :default: :flavor
253
- :account: true
254
- :list_values:
255
- :query_type: :query_call # Will execute a query on flavor, query_params is empty for all.
256
- :object: :flavor
257
- :value: :name
258
- :validate: :list_strict
259
- :ask_step: 2
260
- :image_name:
261
- :desc: "Image name"
262
- :explanation: |-
263
- Ubuntu image used to create Maestro and all forge boxes. Originally, Maestro uses 'Ubuntu Precise 12.04.4 LTS Server 64-bit'.
264
- You need to choose the appropriate image to make Maestro & boxes to boot normally.
265
- :account: true
266
- :ask_step: 2
267
- :list_values:
268
- :query_type: :query_call # Will execute a query on flavor, query_params is empty for all. No filter currently working.
269
- :object: :image
270
- :value: :name
271
- :validate: :list_strict
272
- :ask_step: 2
273
- :ssh_user:
274
- :desc: "User name for ssh connection of your selected image."
275
- :explanation: |-
276
- The image name you have selected has a unique SSH Account access.
277
-
278
- Thanks to the name of the image, setup assume the account name to use.
279
- If this name is incoherent with the image you choosed, please update it.
280
-
281
- Checking image '<%= config[:image_name] %>'...
282
- :account: true
283
- :ask_step: 2
284
- :after: :image_name
285
- :list_values:
286
- :query_type: :process_call # Will execute a process to query on image
287
- :query_call: :setup_ssh_user # and return the list of images and a default value.
288
- :query_params: # Transmitted as hParams
289
- :image_name: '<%= config[:image_name] %>'
290
- :ports:
291
- :desc: "List of security group rules (1 port or range of ports) to open to the external network."
292
- :branch:
293
- :desc: "Branch to use to build your forge"
294
- :bootstrap_dirs:
295
- :desc: "Additional bootstrap directories (separated by space) to add in the bootstrap loop."
296
- :bootstrap_extra_dir:
297
- :desc: "Additional bootstrap directory to add in the bootstrap loop, before :bootstrap_dirs and after maestro default bootstrap directory."
data/lib/boot.rb CHANGED
@@ -53,9 +53,9 @@ module Forj
53
53
 
54
54
  def self.load_options(options, options_map)
55
55
  options_map.each do |opt_key, ac_key|
56
- unless options[opt_key].nil?
57
- value = yield(opt_key, options[opt_key])
58
- @account.set(ac_key, options[opt_key]) unless value.nil?
56
+ if options.key?(opt_key.to_s)
57
+ value = yield(opt_key, options[opt_key.to_s])
58
+ @account.set(ac_key, value) unless value.nil?
59
59
  end
60
60
  end
61
61
  end
@@ -99,10 +99,12 @@ module Forj
99
99
  @account[:instance_name], @account[:blueprint])
100
100
  end
101
101
  end
102
+ end
103
+ # rubocop: disable Metrics/CyclomaticComplexity
104
+ # rubocop: disable Metrics/MethodLength
102
105
 
103
- # rubocop: disable Metrics/CyclomaticComplexity
104
- # rubocop: disable Metrics/MethodLength
105
-
106
+ #
107
+ module Boot
106
108
  # Boot process
107
109
  def self.boot(blueprint, on_or_name, deprecated_name, options)
108
110
  @account = Lorj::Account.new(options[:config])
@@ -125,6 +127,9 @@ module Forj
125
127
  @account[:account_name], @account[:account_name])
126
128
  end
127
129
 
130
+ options = options.to_h
131
+ options['tb_path'] = nil if options.key?('test_box') &&
132
+ !options.key?('tb_path')
128
133
  options_map = { :infra => :infra_repo,
129
134
  :key_name => :keypair_name,
130
135
  :key_path => :keypair_path,
@@ -135,22 +140,10 @@ module Forj
135
140
  :maestro_repo => :maestro_repo,
136
141
  :branch => :branch,
137
142
  :test_box => :test_box,
143
+ :tb_path => :test_box_path,
138
144
  :extra_metadata => :extra_metadata }
139
145
 
140
- load_options(options, options_map) do |key, value|
141
- case key
142
- when :test_box
143
- path = File.expand_path(value)
144
- return path if File.directory?(path)
145
- return nil
146
- end
147
- value
148
- end
149
-
150
- PrcLib.warning(
151
- 'test_box is currently disabled in this version.' \
152
- 'It will be re-activated in newer version.'
153
- ) if options[:test_box]
146
+ load_options(options, options_map) { |k, v| complete_boot_options(k, v) }
154
147
 
155
148
  validate_keypath(options)
156
149
 
@@ -166,4 +159,91 @@ module Forj
166
159
  o_cloud.create(:forge)
167
160
  end
168
161
  end
162
+ # rubocop: enable Metrics/CyclomaticComplexity
163
+ # rubocop: enable Metrics/MethodLength
164
+
165
+ #
166
+ module Boot
167
+ # Take care of special options cases for boot.
168
+ def self.complete_boot_options(key, value)
169
+ case key
170
+ when :test_box
171
+ value = tb_repo_detect(value)
172
+ when :tb_path
173
+ value = tb_bin_detect(value)
174
+ end
175
+ value
176
+ end
177
+
178
+ # Function to check the repository path passed.
179
+ def self.tb_repo_detect(paths)
180
+ res = {}
181
+ paths.each do |path|
182
+ cmd = <<-CMD
183
+ cd "#{path}"
184
+ git rev-parse --show-toplevel 2>/dev/null 1>&2
185
+ if [ $? -ne 0 ]
186
+ then
187
+ exit 1
188
+ fi
189
+ REPO_TO_ADD="$(LANG= git remote show origin -n |
190
+ awk '$1 ~ /Fetch/ { print $3 }')"
191
+ if [ "$REPO_TO_ADD" = "" ]
192
+ then
193
+ exit 1
194
+ fi
195
+ echo $REPO_TO_ADD
196
+ pwd
197
+ CMD
198
+ cmd_res = `#{cmd}`.split
199
+ # For any reason, $CHILD_STATUS is empty, while $? is not.
200
+ # Ruby bug. tested with:
201
+ # ruby 2.0.0p353 (2013-11-22 revision 43784) [x86_64-linux]
202
+ # rubocop: disable Style/SpecialGlobalVars
203
+ next unless $?.exitstatus == 0
204
+ # rubocop: enable Style/SpecialGlobalVars
205
+ repo_found = cmd_res[0].match(%r{.*/(.*)(.git)?})
206
+ next unless repo_found
207
+ res[repo_found[1]] = cmd_res[1]
208
+ end
209
+ res
210
+ end
211
+
212
+ # function to detect if test-box.sh is runnable
213
+ #
214
+ # It returns the script to execute.
215
+ def self.tb_bin_detect(tb_path)
216
+ tb_path = ENV['TEST_BOX'] if tb_path.nil?
217
+ tb_path = File.expand_path(tb_path) unless tb_path.nil?
218
+
219
+ script = 'test-box.sh'
220
+ if tb_path && File.directory?(tb_path)
221
+ script_found = tb_check_bin(tb_path)
222
+ script = File.expand_path(File.join(tb_path, script))
223
+ if script_found.nil?
224
+ PrcLib.error("Test-box: '%s' is not a valid runnable script. "\
225
+ 'test-box is disabled.', script)
226
+ return nil
227
+ end
228
+ return script_found
229
+ end
230
+
231
+ script_found = nil
232
+
233
+ ENV['PATH'].split(':').each do |path|
234
+ script_found = tb_check_bin(path)
235
+ break unless script_found.nil?
236
+ end
237
+
238
+ script_found
239
+ end
240
+
241
+ # Script to check the bin and path
242
+ def self.tb_check_bin(tb_path)
243
+ script = 'test-box.sh'
244
+ script = File.expand_path(File.join(tb_path, script))
245
+ return script if File.executable?(script)
246
+ nil
247
+ end
248
+ end
169
249
  end