poise-ruby 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.kitchen.travis.yml +9 -0
  4. data/.kitchen.yml +9 -0
  5. data/.travis.yml +20 -0
  6. data/.yardopts +7 -0
  7. data/Berksfile +28 -0
  8. data/Gemfile +33 -0
  9. data/LICENSE +201 -0
  10. data/README.md +290 -0
  11. data/Rakefile +17 -0
  12. data/chef/attributes/default.rb +23 -0
  13. data/chef/recipes/default.rb +19 -0
  14. data/lib/poise_ruby.rb +24 -0
  15. data/lib/poise_ruby/cheftie.rb +18 -0
  16. data/lib/poise_ruby/error.rb +21 -0
  17. data/lib/poise_ruby/resources.rb +29 -0
  18. data/lib/poise_ruby/resources/bundle_install.rb +221 -0
  19. data/lib/poise_ruby/resources/ruby_execute.rb +91 -0
  20. data/lib/poise_ruby/resources/ruby_gem.rb +118 -0
  21. data/lib/poise_ruby/resources/ruby_runtime.rb +87 -0
  22. data/lib/poise_ruby/ruby_command_mixin.rb +59 -0
  23. data/lib/poise_ruby/ruby_providers.rb +32 -0
  24. data/lib/poise_ruby/ruby_providers/base.rb +116 -0
  25. data/lib/poise_ruby/ruby_providers/chef.rb +53 -0
  26. data/lib/poise_ruby/ruby_providers/scl.rb +77 -0
  27. data/lib/poise_ruby/ruby_providers/system.rb +115 -0
  28. data/lib/poise_ruby/version.rb +20 -0
  29. data/poise-ruby.gemspec +41 -0
  30. data/test/cookbooks/poise-ruby_test/metadata.rb +18 -0
  31. data/test/cookbooks/poise-ruby_test/recipes/bundle_install.rb +102 -0
  32. data/test/cookbooks/poise-ruby_test/recipes/default.rb +85 -0
  33. data/test/gemfiles/chef-12.gemfile +19 -0
  34. data/test/gemfiles/master.gemfile +23 -0
  35. data/test/integration/default/serverspec/bundle_install_spec.rb +73 -0
  36. data/test/integration/default/serverspec/default_spec.rb +46 -0
  37. data/test/spec/resources/bundle_install_spec.rb +306 -0
  38. data/test/spec/resources/ruby_execute_spec.rb +78 -0
  39. data/test/spec/ruby_command_mixin_spec.rb +34 -0
  40. data/test/spec/spec_helper.rb +18 -0
  41. metadata +155 -0
@@ -0,0 +1,17 @@
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 'poise_boiler/rakefile'
@@ -0,0 +1,23 @@
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
+ # Default inversion options.
18
+ default['poise-ruby']['provider'] = 'auto'
19
+ default['poise-ruby']['options'] = {}
20
+
21
+ # Used for the default recipe.
22
+ default['poise-ruby']['install_ruby'] = true
23
+ default['poise-ruby']['install_chef_ruby'] = true
@@ -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
+ # Default runtimes, last one will be the default.
18
+ ruby_runtime('chef') { provider :chef } if node['poise-ruby']['install_chef_ruby']
19
+ ruby_runtime 'ruby' if node['poise-ruby']['install_ruby']
@@ -0,0 +1,24 @@
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 PoiseRuby
19
+ autoload :Error, 'poise_ruby/error'
20
+ autoload :Resources, 'poise_ruby/resources'
21
+ autoload :RubyCommandMixin, 'poise_ruby/ruby_command_mixin'
22
+ autoload :RubyProviders, 'poise_ruby/ruby_providers'
23
+ autoload :VERSION, 'poise_ruby/version'
24
+ end
@@ -0,0 +1,18 @@
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 'poise_ruby/resources'
18
+ require 'poise_ruby/ruby_providers'
@@ -0,0 +1,21 @@
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 PoiseRuby
19
+ class Error < ::Exception
20
+ end
21
+ end
@@ -0,0 +1,29 @@
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 'poise_ruby/resources/bundle_install'
18
+ require 'poise_ruby/resources/ruby_execute'
19
+ require 'poise_ruby/resources/ruby_gem'
20
+ require 'poise_ruby/resources/ruby_runtime'
21
+
22
+
23
+ module PoiseRuby
24
+ # Chef resources and providers for poise-ruby.
25
+ #
26
+ # @since 2.0.0
27
+ module Resources
28
+ end
29
+ end
@@ -0,0 +1,221 @@
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/mixin/shell_out'
18
+ require 'chef/mixin/which'
19
+ require 'chef/provider'
20
+ require 'chef/resource'
21
+ require 'poise'
22
+
23
+ require 'poise_ruby/error'
24
+ require 'poise_ruby/ruby_command_mixin'
25
+
26
+
27
+ module PoiseRuby
28
+ module Resources
29
+ # (see BundleInstall::Resource)
30
+ # @since 2.0.0
31
+ module BundleInstall
32
+ # A `bundle_install` resource to install a [Bundler](http://bundler.io/)
33
+ # Gemfile.
34
+ #
35
+ # @provides bundle_install
36
+ # @action install
37
+ # @action update
38
+ # @note
39
+ # This resource is not idempotent itself, it will always run `bundle
40
+ # install`.
41
+ # @example
42
+ # bundle_install '/opt/my_app' do
43
+ # gem_path '/usr/local/bin/gem'
44
+ # end
45
+ class Resource < Chef::Resource
46
+ include Poise
47
+ provides(:bundle_install)
48
+ actions(:install, :update)
49
+ include PoiseRuby::RubyCommandMixin
50
+
51
+ # @!attribute path
52
+ # Path to the Gemfile or to a directory that contains a Gemfile.
53
+ # @return [String]
54
+ attribute(:path, kind_of: String, name_attribute: true)
55
+ # @!attribute binstubs
56
+ # Enable binstubs. If set to a string it is the path to generate
57
+ # stubs in.
58
+ # @return [Boolean, String]
59
+ attribute(:binstubs, kind_of: [TrueClass, String])
60
+ # @!attribute deployment
61
+ # Enable deployment mode.
62
+ # @return [Boolean]
63
+ attribute(:deployment, equal_to: [true, false], default: false)
64
+ # @!attribute jobs
65
+ # Number of parallel installations to run.
66
+ # @return [String, Integer]
67
+ attribute(:jobs, kind_of: [String, Integer])
68
+ # @!attribute retry
69
+ # Number of times to retry failed installations.
70
+ # @return [String, Integer]
71
+ attribute(:retry, kind_of: [String, Integer])
72
+ # @!attribute user
73
+ # User to run bundler as.
74
+ # @return [String, Integery, nil]
75
+ attribute(:user, kind_of: [String, Integer, NilClass])
76
+ # @!attribute vendor
77
+ # Enable local vendoring. This maps to the `--path` option in bundler,
78
+ # but that attribute name is already used.
79
+ # @return [Boolean, String]
80
+ attribute(:vendor, kind_of: [TrueClass, String])
81
+ # @!attribute without
82
+ # Group or groups to not install.
83
+ # @return [String, Array<String>]
84
+ attribute(:without, kind_of: [Array, String])
85
+
86
+ # The path to the `bundle` binary for this installation. This is an
87
+ # output property.
88
+ #
89
+ # @return [String]
90
+ # @example
91
+ # execute "#{resources('bundle_install[/opt/myapp]').bundler_binary} vendor"
92
+ def bundler_binary
93
+ @bundler_binary ||= provider_for_action(:bundler_binary).bundler_binary
94
+ end
95
+
96
+ # The path to the Gemfile for this installation. This is an output
97
+ # property.
98
+ #
99
+ # @return [String]
100
+ # @example
101
+ # file resources('bundle_install[/opt/myapp]').gemfile_path do
102
+ # owner 'root'
103
+ # end
104
+ def gemfile_path
105
+ @gemfile_path ||= provider_for_action(:gemfile_path).gemfile_path
106
+ end
107
+ end
108
+
109
+ # The default provider for the `bundle_install` resource.
110
+ #
111
+ # @see Resource
112
+ class Provider < Chef::Provider
113
+ include Poise
114
+ provides(:bundle_install)
115
+ include PoiseRuby::RubyCommandMixin
116
+
117
+ # Install bundler and the gems in the Gemfile.
118
+ def action_install
119
+ run_bundler('install')
120
+ end
121
+
122
+ # Install bundler and update the gems in the Gemfile.
123
+ def action_update
124
+ run_bundler('update')
125
+ end
126
+
127
+ # Return the absolute path to the correct bundle binary to run.
128
+ #
129
+ # @return [String]
130
+ def bundler_binary
131
+ @bundler_binary ||= ::File.join(gem_bindir, 'bundle')
132
+ end
133
+
134
+ # Find the absolute path to the Gemfile. This mirrors bundler's internal
135
+ # search logic by scanning up to parent folder as needed.
136
+ #
137
+ # @return [String]
138
+ def gemfile_path
139
+ @gemfile_path ||= begin
140
+ path = ::File.expand_path(new_resource.path)
141
+ if ::File.file?(path)
142
+ # We got a path to a real file, use that.
143
+ path
144
+ else
145
+ # Walk back until path==dirname(path) meaning we are at the root
146
+ while path != (next_path = ::File.dirname(path))
147
+ possible_path = ::File.join(path, 'Gemfile')
148
+ return possible_path if ::File.file?(possible_path)
149
+ path = next_path
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ # Install the gems in the Gemfile.
158
+ def run_bundler(command)
159
+ return converge_by "Run bundle #{command}" if whyrun_mode?
160
+ cmd = ruby_shell_out!(bundler_command(command), environment: {'BUNDLE_GEMFILE' => gemfile_path}, user: new_resource.user)
161
+ # Look for a line like 'Installing $gemname $version' to know if we did anything.
162
+ if cmd.stdout.include?('Installing')
163
+ new_resource.updated_by_last_action(true)
164
+ end
165
+ end
166
+
167
+ # Parse out the value for Gem.bindir. This is so complicated to minimize
168
+ # the required configuration on the resource combined with gem having
169
+ # terrible output formats.
170
+ #
171
+ # @return [String]
172
+ def gem_bindir
173
+ cmd = ruby_shell_out!(new_resource.gem_binary, 'environment')
174
+ # Parse a line like:
175
+ # - EXECUTABLE DIRECTORY: /usr/local/bin
176
+ matches = cmd.stdout.scan(/EXECUTABLE DIRECTORY: (.*)$/).first
177
+ if matches
178
+ matches.first
179
+ else
180
+ raise PoiseRuby::Error.new("Cannot find EXECUTABLE DIRECTORY: #{cmd.stdout}")
181
+ end
182
+ end
183
+
184
+ # Command line options for the bundle install.
185
+ #
186
+ # @return [Array<String>]
187
+ def bundler_options
188
+ [].tap do |opts|
189
+ if new_resource.binstubs
190
+ opts << "--binstubs" + (new_resource.binstubs.is_a?(String) ? "=#{new_resource.binstubs}" : '')
191
+ end
192
+ if new_resource.vendor
193
+ opts << "--path=" + (new_resource.vendor.is_a?(String) ? new_resource.vendor : 'vendor/bundle')
194
+ end
195
+ if new_resource.deployment
196
+ opts << '--deployment'
197
+ end
198
+ if new_resource.jobs
199
+ opts << "--jobs=#{new_resource.jobs}"
200
+ end
201
+ if new_resource.retry
202
+ opts << "--retry=#{new_resource.retry}"
203
+ end
204
+ if new_resource.without
205
+ opts << '--without'
206
+ opts.insert(-1, *new_resource.without)
207
+ end
208
+ end
209
+ end
210
+
211
+ # Command array to run when installing the Gemfile.
212
+ #
213
+ # @return [Array<String>]
214
+ def bundler_command(command)
215
+ [bundler_binary, command] + bundler_options
216
+ end
217
+
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,91 @@
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 'chef/mash'
20
+ require 'chef/provider/execute'
21
+ require 'chef/resource/execute'
22
+ require 'poise'
23
+
24
+ require 'poise_ruby/ruby_command_mixin'
25
+
26
+
27
+ module PoiseRuby
28
+ module Resources
29
+ # (see RubyExecute::Resource)
30
+ # @since 2.0.0
31
+ module RubyExecute
32
+ # A `ruby_execute` resource to run Ruby scripts and commands.
33
+ #
34
+ # @provides ruby_execute
35
+ # @action run
36
+ # @example
37
+ # ruby_execute 'myapp.rb' do
38
+ # user 'myuser'
39
+ # end
40
+ class Resource < Chef::Resource::Execute
41
+ include Poise
42
+ provides(:ruby_execute)
43
+ actions(:run)
44
+ include PoiseRuby::RubyCommandMixin
45
+
46
+ # @!attribute parent_bundle
47
+ # Optional bundle_install resource to run `bundle exec` against.
48
+ # @return [PoiseRuby::Resources::BundleInstall::Resource]
49
+ parent_attribute(:bundle, type: :bundle_install, optional: true, auto: false)
50
+ end
51
+
52
+ # The default provider for `ruby_execute`.
53
+ #
54
+ # @see Resource
55
+ # @provides ruby_execute
56
+ class Provider < Chef::Provider::Execute
57
+ provides(:ruby_execute)
58
+
59
+ private
60
+
61
+ # Command to pass to shell_out.
62
+ #
63
+ # @return [String, Array<String>]
64
+ def command
65
+ prefix = [new_resource.ruby]
66
+ if new_resource.parent_bundle
67
+ prefix << new_resource.parent_bundle.bundler_binary
68
+ prefix << 'exec'
69
+ end
70
+ if new_resource.command.is_a?(Array)
71
+ prefix + new_resource.command
72
+ else
73
+ "#{Shellwords.join(prefix)} #{new_resource.command}"
74
+ end
75
+ end
76
+
77
+ # Environment variables to pass to shell_out.
78
+ #
79
+ # @return [Hash]
80
+ def environment
81
+ Mash.new.tap do |environment|
82
+ environment.update(new_resource.parent_ruby.ruby_environment) if new_resource.parent_ruby
83
+ environment['BUNDLE_GEMFILE'] = new_resource.parent_bundle.gemfile_path if new_resource.parent_bundle
84
+ environment.update(new_resource.environment) if new_resource.environment
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+ end
91
+ end