poise-languages 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,202 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'chef/resource'
18
+ require 'chef/provider'
19
+ require 'poise'
20
+
21
+
22
+ module PoiseLanguages
23
+ module System
24
+ # A `poise_language_system` resource to manage installing a language from
25
+ # system packages. This is an internal implementation detail of
26
+ # poise-languages.
27
+ #
28
+ # @api private
29
+ # @since 1.0
30
+ # @provides poise_languages_system
31
+ # @action install
32
+ # @action upgrade
33
+ # @action uninstall
34
+ class Resource < Chef::Resource
35
+ include Poise
36
+ provides(:poise_languages_system)
37
+ actions(:install, :upgrade, :uninstall)
38
+
39
+ # @!attribute package_name
40
+ # Name of the main package for the language.
41
+ # @return [String]
42
+ attribute(:package_name, kind_of: String, name_attribute: true)
43
+ # @!attribute dev_package
44
+ # Name of the development headers package, or false to disable
45
+ # installing headers. By default computed from {package_name}.
46
+ # @return [String, false]
47
+ attribute(:dev_package, kind_of: [String, FalseClass], default: lazy { default_dev_package })
48
+ # @!attribute dev_package_overrides
49
+ # A hash of override names for dev packages that don't match the normal
50
+ # naming scheme.
51
+ # @return [Hash<String, String>]
52
+ attribute(:dev_package_overrides, kind_of: Hash, default: lazy { {} })
53
+ # @!attribute package_version
54
+ # Version of the package(s) to install. This is distinct from {version},
55
+ # and is the specific version package version, not the language version.
56
+ # By default this is unset meaning the latest version will be used.
57
+ # @return [String, nil]
58
+ attribute(:package_version, kind_of: [String, NilClass])
59
+ # @!attribute parent
60
+ # Resource for the language runtime. Used only for messages.
61
+ # @return [Chef::Resource]
62
+ attribute(:parent, kind_of: Chef::Resource, required: true)
63
+ # @!attributes version
64
+ # Language version prefix. This prefix determines which version of the
65
+ # language to install, following prefix matching rules.
66
+ # @return [String]
67
+ attribute(:version, kind_of: String, default: '')
68
+
69
+ # Compute the default package name for the development headers.
70
+ #
71
+ # @return [String]
72
+ def default_dev_package
73
+ # Check for an override.
74
+ return dev_package_overrides[package_name] if dev_package_overrides.include?(package_name)
75
+ suffix = node.value_for_platform_family(debian: '-dev', rhel: '-devel', fedora: '-devel')
76
+ # Platforms like Arch and Gentoo don't need this anyway. I've got no
77
+ # clue how Amazon Linux does this.
78
+ if suffix
79
+ package_name + suffix
80
+ else
81
+ nil
82
+ end
83
+ end
84
+ end
85
+
86
+ # The default provider for `poise_languages_system`.
87
+ #
88
+ # @api private
89
+ # @since 1.0
90
+ # @see Resource
91
+ # @provides poise_languages_system
92
+ class Provider < Chef::Provider
93
+ include Poise
94
+ provides(:poise_languages_system)
95
+
96
+ # The `install` action for the `poise_languages_system` resource.
97
+ #
98
+ # @return [void]
99
+ def action_install
100
+ run_package_action(:install)
101
+ end
102
+
103
+ # The `upgrade` action for the `poise_languages_system` resource.
104
+ #
105
+ # @return [void]
106
+ def action_upgrade
107
+ run_package_action(:upgrade)
108
+ end
109
+
110
+ # The `uninstall` action for the `poise_languages_system` resource.
111
+ #
112
+ # @return [void]
113
+ def action_uninstall
114
+ action = node.platform_family?('debian') ? :purge : :remove
115
+ package_resources(action).each do |resource|
116
+ resource.run_action(action)
117
+ new_resource.updated_by_last_action(true) if resource.updated_by_last_action?
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ # Create package resource objects for all needed packages. These are created
124
+ # directly and not added to the resource collection.
125
+ #
126
+ # @return [Array<Chef::Resource::Package>]
127
+ def package_resources(action)
128
+ packages = {new_resource.package_name => new_resource.package_version}
129
+ # If we are supposed to install the dev package, grab it using the same
130
+ # version as the main package.
131
+ if new_resource.dev_package
132
+ packages[new_resource.dev_package] = new_resource.package_version
133
+ end
134
+
135
+ Chef::Log.debug("[#{new_resource.parent}] Building package resource using #{packages.inspect}.")
136
+ @package_resource ||= if node.platform_family?('rhel', 'fedora', 'amazon')
137
+ # @todo Can't use multi-package mode with yum pending https://github.com/chef/chef/issues/3476.
138
+ packages.map do |name, version|
139
+ Chef::Resource::Package.new(name, run_context).tap do |r|
140
+ r.version(version)
141
+ r.action(action)
142
+ r.declared_type = :package
143
+ end
144
+ end
145
+ else
146
+ [Chef::Resource::Package.new(packages.keys, run_context).tap do |r|
147
+ r.version(packages.values)
148
+ r.action(action)
149
+ r.declared_type = :package
150
+ end]
151
+ end
152
+ end
153
+
154
+ # Run the requested action for all package resources. This exists because
155
+ # we inject our version check in to the provider directly and I want to
156
+ # only run the provider action once for performance. It is otherwise
157
+ # mostly a stripped down version of Chef::Resource#run_action.
158
+ #
159
+ # @param action [Symbol] Action to run on all package resources.
160
+ # @return [void]
161
+ def run_package_action(action)
162
+ package_resources(action).each do |resource|
163
+ # Reset it so we have a clean baseline.
164
+ resource.updated_by_last_action(false)
165
+ # Grab the provider.
166
+ provider = resource.provider_for_action(action)
167
+ provider.action = action
168
+ # Check the candidate version if needed
169
+ patch_load_current_resource!(provider, new_resource.version)
170
+ # Run our action.
171
+ Chef::Log.debug("[#{new_resource.parent}] Running #{provider} with #{action}")
172
+ provider.run_action(action)
173
+ # Check updated flag.
174
+ new_resource.updated_by_last_action(true) if resource.updated_by_last_action?
175
+ end
176
+ end
177
+
178
+ # Hack a provider object to run our verification code.
179
+ #
180
+ # @param provider [Chef::Provider] Provider object to patch.
181
+ # @param version [String] Language version prefix to check for.
182
+ # @return [void]
183
+ def patch_load_current_resource!(provider, version)
184
+ # Create a closure module and inject it.
185
+ provider.extend Module.new {
186
+ # Patch load_current_resource to run our verification logic after
187
+ # the normal code.
188
+ define_method(:load_current_resource) do
189
+ super().tap do |_|
190
+ each_package do |package_name, new_version, current_version, candidate_version|
191
+ unless candidate_version.start_with?(version)
192
+ raise PoiseLanguages::Error.new("Package #{package_name} would install #{candidate_version}, which does not match #{version.empty? ? version.inspect : version}. Please set the package_name or package_version provider options.")
193
+ end
194
+ end
195
+ end
196
+ end
197
+ }
198
+ end
199
+
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,49 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'shellwords'
18
+
19
+ require 'poise_languages/utils/which'
20
+
21
+
22
+ module PoiseLanguages
23
+ module Utils
24
+ include Which
25
+ extend self
26
+
27
+ # Convert the executable in a string or array command to an absolute path.
28
+ #
29
+ # @param cmd [String, Array<String>] Command to fix up.
30
+ # @param path [String, nil] Replacement $PATH for executable lookup.
31
+ # @return [String, Array<String>]
32
+ def absolute_command(cmd, path: nil)
33
+ was_array = cmd.is_a?(Array)
34
+ cmd = if was_array
35
+ cmd.dup
36
+ else
37
+ Shellwords.split(cmd)
38
+ end
39
+ # Don't try to touch anything if the first value looks like a flag.
40
+ if cmd.first && !cmd.first.start_with?('-')
41
+ # If which returns false, just leave it I guess.
42
+ cmd[0] = which(cmd.first, path: path) || cmd.first
43
+ end
44
+ cmd = Shellwords.join(cmd) unless was_array
45
+ cmd
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+
18
+ module PoiseLanguages
19
+ module Utils
20
+ # Replacement module for Chef::Mixin::Which with a slight improvement.
21
+ #
22
+ # @since 1.0.0
23
+ # @see Which#which
24
+ module Which
25
+ extend self
26
+
27
+ # A replacement for Chef::Mixin::Which#which that allows using something
28
+ # other than an environment variable if needed.
29
+ #
30
+ # @param cmd [String] Executable to search for.
31
+ # @param extra_path [Array<String>] Extra directories to always search.
32
+ # @param path [String, nil] Replacement $PATH value.
33
+ # @return [String, false]
34
+ def which(cmd, extra_path: %w{/bin /usr/bin /sbin /usr/sbin}, path: nil)
35
+ # Allow passing something other than the real env var.
36
+ path ||= ENV['PATH']
37
+ # Based on Chef::Mixin::Which#which
38
+ # Copyright 2010-2015, Chef Softare, Inc.
39
+ paths = path.split(File::PATH_SEPARATOR) + extra_path
40
+ paths.each do |candidate_path|
41
+ filename = ::File.join(candidate_path, cmd)
42
+ return filename if ::File.executable?(filename)
43
+ end
44
+ false
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+
18
+ module PoiseLanguages
19
+ VERSION = '1.0.0'
20
+ end
@@ -0,0 +1,40 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ lib = File.expand_path('../lib', __FILE__)
18
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
19
+ require 'poise_languages/version'
20
+
21
+ Gem::Specification.new do |spec|
22
+ spec.name = 'poise-languages'
23
+ spec.version = PoiseLanguages::VERSION
24
+ spec.authors = ['Noah Kantrowitz']
25
+ spec.email = %w{noah@coderanger.net}
26
+ spec.description = "A Chef cookbook to help writing language cooobkoks."
27
+ spec.summary = spec.description
28
+ spec.homepage = 'https://github.com/poise/poise-languages'
29
+ spec.license = 'Apache 2.0'
30
+
31
+ spec.files = `git ls-files`.split($/)
32
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
33
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
34
+ spec.require_paths = %w{lib}
35
+
36
+ spec.add_dependency 'halite', '~> 1.0'
37
+ spec.add_dependency 'poise', '~> 2.3'
38
+
39
+ spec.add_development_dependency 'poise-boiler', '~> 1.0'
40
+ end
@@ -0,0 +1,19 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ eval_gemfile File.expand_path('../../../Gemfile', __FILE__)
18
+
19
+ gem 'chef', '~> 12.0'
@@ -0,0 +1,22 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ eval_gemfile File.expand_path('../../../Gemfile', __FILE__)
18
+
19
+ gem 'chef', github: 'chef/chef'
20
+ gem 'halite', github: 'poise/halite'
21
+ gem 'poise', github: 'poise/poise'
22
+ gem 'poise-boiler', github: 'poise/poise-boiler'
@@ -0,0 +1,328 @@
1
+ #
2
+ # Copyright 2015, Noah Kantrowitz
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'spec_helper'
18
+
19
+ describe PoiseLanguages::Command::Mixin do
20
+ resource(:mylang_runtime) do
21
+ include Poise(container: true)
22
+ attribute(:mylang_binary, default: '/binary')
23
+ attribute(:mylang_environment, default: {'MYLANG_PATH' => '/thing'})
24
+ end
25
+ provider(:mylang_runtime)
26
+ let(:runtime) { chef_run.mylang_runtime('parent') }
27
+
28
+ describe PoiseLanguages::Command::Mixin::Resource do
29
+ resource(:poise_test) do
30
+ klass = described_class
31
+ include Module.new {
32
+ include klass
33
+ language_command_mixin(:mylang)
34
+ }
35
+ def which(name)
36
+ "/which/#{name}"
37
+ end
38
+ end
39
+ provider(:poise_test)
40
+
41
+ describe '#parent_$name' do
42
+ context 'with a parent' do
43
+ recipe do
44
+ mylang_runtime 'parent'
45
+ poise_test 'test'
46
+ end
47
+ it { is_expected.to run_poise_test('test').with(parent_mylang: chef_run.mylang_runtime('parent')) }
48
+ end # /context with a parent
49
+
50
+ context 'with no parent' do
51
+ recipe do
52
+ poise_test 'test'
53
+ end
54
+ it { is_expected.to run_poise_test('test').with(parent_mylang: nil) }
55
+ end # /context with no parent
56
+
57
+ context 'with two containers' do
58
+ recipe do
59
+ mylang_runtime 'parent'
60
+ poise_test 'test'
61
+ mylang_runtime 'other'
62
+ end
63
+
64
+ it { is_expected.to run_poise_test('test').with(parent_mylang: chef_run.mylang_runtime('parent')) }
65
+ end # /context with two containers
66
+ end # /describe #parent_$name
67
+
68
+ describe '#timeout' do
69
+ recipe do
70
+ poise_test 'test' do
71
+ # Be explicit because Kernel#timeout is part of the timeout.rb lib.
72
+ self.timeout(123)
73
+ end
74
+ end
75
+
76
+ context 'with timeout enabled' do
77
+ it { is_expected.to run_poise_test('test').with(timeout: 123) }
78
+ end # /context with timeout enabled
79
+
80
+ context 'with timeout disabled' do
81
+ resource(:poise_test) do
82
+ klass = described_class
83
+ include Module.new {
84
+ include klass
85
+ language_command_mixin(:poise, timeout: false)
86
+ }
87
+ end
88
+ it { expect { subject }.to raise_error NoMethodError }
89
+ end # /context with timeout disabled
90
+ end # /describe #timeout
91
+
92
+ describe '#$name' do
93
+ context 'with an implicit parent' do
94
+ recipe do
95
+ mylang_runtime 'parent'
96
+ poise_test 'test'
97
+ end
98
+
99
+ it { is_expected.to run_poise_test('test').with(parent_mylang: runtime, mylang: '/binary') }
100
+ end # /context with an implicit parent
101
+
102
+ context 'with a parent resource' do
103
+ recipe do
104
+ r = mylang_runtime 'parent'
105
+ poise_test 'test' do
106
+ mylang r
107
+ end
108
+ end
109
+
110
+ it { is_expected.to run_poise_test('test').with(parent_mylang: runtime, mylang: '/binary') }
111
+ end # /context with a parent resource
112
+
113
+ context 'with a parent resource name' do
114
+ recipe do
115
+ mylang_runtime 'parent'
116
+ poise_test 'test' do
117
+ mylang 'parent'
118
+ end
119
+ end
120
+
121
+ it { is_expected.to run_poise_test('test').with(parent_mylang: runtime, mylang: '/binary') }
122
+ end # /context with a parent resource name
123
+
124
+ context 'with a parent resource name that looks like a path' do
125
+ let(:runtime) { chef_run.mylang_runtime('/usr/bin/other') }
126
+ recipe do
127
+ mylang_runtime '/usr/bin/other' do
128
+ mylang_binary name
129
+ end
130
+ poise_test 'test' do
131
+ mylang '/usr/bin/other'
132
+ end
133
+ end
134
+
135
+ it { is_expected.to run_poise_test('test').with(parent_mylang: runtime, mylang: '/usr/bin/other') }
136
+ end # /context with a parent resource name that looks like a path
137
+
138
+ context 'with a path' do
139
+ recipe do
140
+ poise_test 'test' do
141
+ mylang '/usr/bin/other'
142
+ end
143
+ end
144
+
145
+ it { is_expected.to run_poise_test('test').with(parent_mylang: nil, mylang: '/usr/bin/other') }
146
+ end # /context with a path
147
+
148
+ context 'with a path and an implicit parent' do
149
+ recipe do
150
+ mylang_runtime 'parent'
151
+ poise_test 'test' do
152
+ mylang '/usr/bin/other'
153
+ end
154
+ end
155
+
156
+ it { is_expected.to run_poise_test('test').with(parent_mylang: runtime, mylang: '/usr/bin/other') }
157
+ end # /context with a path and an implicit parent
158
+
159
+ context 'with an invalid parent' do
160
+ recipe do
161
+ poise_test 'test' do
162
+ mylang 'test'
163
+ end
164
+ end
165
+
166
+ it { expect { subject }.to raise_error Chef::Exceptions::ResourceNotFound }
167
+ end # /context with an invalid parent
168
+
169
+ context 'with no parent' do
170
+ recipe do
171
+ poise_test 'test'
172
+ end
173
+
174
+ it { is_expected.to run_poise_test('test').with(parent_mylang: nil, mylang: '/which/mylang') }
175
+ end # /context with no parent
176
+ end # /describe #$name
177
+
178
+ describe '#$name_from_parent' do
179
+ context 'with a resource parent' do
180
+ recipe do
181
+ mylang_runtime 'parent'
182
+ r = poise_test 'first' do
183
+ mylang 'parent'
184
+ end
185
+ mylang_runtime 'other'
186
+ poise_test 'test' do
187
+ mylang_from_parent r
188
+ end
189
+ end
190
+
191
+ it { is_expected.to run_poise_test('test').with(parent_mylang: runtime, mylang: '/binary') }
192
+ end # /context with a resource parent
193
+
194
+ context 'with a path parent' do
195
+ recipe do
196
+ r = poise_test 'first' do
197
+ mylang '/other'
198
+ end
199
+ poise_test 'test' do
200
+ mylang_from_parent r
201
+ end
202
+ end
203
+
204
+ it { is_expected.to run_poise_test('test').with(parent_mylang: nil, mylang: '/other') }
205
+ end # /context with a path parent
206
+ end # /describe #$name_from_parent
207
+
208
+ context 'as a method' do
209
+ resource(:poise_test) do
210
+ include Module.new {
211
+ include PoiseLanguages::Command::Mixin::Resource(:mylang)
212
+ }
213
+ end
214
+ recipe do
215
+ mylang_runtime 'parent'
216
+ poise_test 'test'
217
+ end
218
+
219
+ it { is_expected.to run_poise_test('test').with(parent_mylang: runtime, mylang: '/binary') }
220
+ end # /context as a method
221
+ end # /describe PoiseLanguages::Command::Mixin::Resource
222
+
223
+ describe PoiseLanguages::Command::Mixin::Provider do
224
+ resource(:poise_test) do
225
+ include PoiseLanguages::Command::Mixin::Resource(:mylang)
226
+ include Poise::Helpers::LWRPPolyfill
227
+ attribute(:command)
228
+ attribute(:expect)
229
+ attribute(:options, default: [])
230
+ def which(name)
231
+ "/which/#{name}"
232
+ end
233
+ end
234
+
235
+ describe '#$name_shell_out' do
236
+ provider(:poise_test) do
237
+ klass = described_class
238
+ include Module.new {
239
+ include klass
240
+ language_command_mixin(:mylang)
241
+ }
242
+ def action_run
243
+ expect(self).to receive(:shell_out).with(*new_resource.expect)
244
+ mylang_shell_out(new_resource.command, *new_resource.options)
245
+ end
246
+ end
247
+
248
+ context 'with a parent' do
249
+ recipe do
250
+ mylang_runtime 'parent'
251
+ poise_test 'test' do
252
+ command 'foo'
253
+ expect ['/binary foo', {environment: {'MYLANG_PATH' => '/thing'}, timeout: 900}]
254
+ end
255
+ end
256
+ it { run_chef }
257
+ end # /context with a parent
258
+
259
+ context 'without a parent' do
260
+ recipe do
261
+ poise_test 'test' do
262
+ command 'foo'
263
+ expect ['/which/mylang foo', {timeout: 900}]
264
+ end
265
+ end
266
+ it { run_chef }
267
+ end # /context without a parent
268
+
269
+ context 'with a timeout' do
270
+ recipe do
271
+ mylang_runtime 'parent'
272
+ poise_test 'test' do
273
+ timeout 300
274
+ command 'foo'
275
+ expect ['/binary foo', {environment: {'MYLANG_PATH' => '/thing'}, timeout: 300}]
276
+ end
277
+ end
278
+ it { run_chef }
279
+ end # /context with a timeout
280
+
281
+ context 'with environment options' do
282
+ recipe do
283
+ mylang_runtime 'parent'
284
+ poise_test 'test' do
285
+ command 'foo'
286
+ options [{environment: {'OTHER' => 'foo'}}]
287
+ expect ['/binary foo', {environment: {'MYLANG_PATH' => '/thing', 'OTHER' => 'foo'}, timeout: 900}]
288
+ end
289
+ end
290
+ it { run_chef }
291
+ end # /context with environment options
292
+
293
+ context 'with an array command' do
294
+ recipe do
295
+ poise_test 'test' do
296
+ command ['foo']
297
+ expect [['/which/mylang', 'foo'], {timeout: 900}]
298
+ end
299
+ end
300
+ it { run_chef }
301
+ end # /context with an array command
302
+ end # /describe #$name_shell_out
303
+
304
+ describe '#$name_shell_out!' do
305
+ provider(:poise_test) do
306
+ klass = described_class
307
+ include Module.new {
308
+ include klass
309
+ language_command_mixin(:mylang)
310
+ }
311
+ def action_run
312
+ fake_output = double()
313
+ expect(fake_output).to receive(:error!)
314
+ expect(self).to receive(:mylang_shell_out).with(*new_resource.expect).and_return(fake_output)
315
+ mylang_shell_out!(new_resource.command, *new_resource.options)
316
+ end
317
+ end
318
+ recipe do
319
+ poise_test 'test' do
320
+ command 'foo'
321
+ expect ['foo']
322
+ end
323
+ end
324
+
325
+ it { run_chef }
326
+ end # /describe #$name_shell_out!
327
+ end # /describe PoiseLanguages::Command::Mixin::Provider
328
+ end