source_install 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +9 -0
  3. data/lib/source_install.rb +342 -0
  4. metadata +102 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 509f4694c4ffef87fcc75fad945512bbab934a03b28c0323cf45347b6663429c
4
+ data.tar.gz: 39105c8b6b84325af2291529f7b3958858a12ce84f75ee9bc617573ff94bf17b
5
+ SHA512:
6
+ metadata.gz: 94093d88f44e85b269f9798ebaa4850fc3ad4c37ea495494497775e691907985f90f60ea3b019cd02e6c4dac4ac4d4d4342a7d04e3d0f0eae146809a9917b48d
7
+ data.tar.gz: a8efdbde9087a6c6d636e39430a4580aaea916fa9fd05948ab3301ebe062d7865eb3f1da58132f2654ea834099f458e313de390bec2a5ad56db83b5e954cd5c6
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017-present The University of Alaska
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,342 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module implements helpers that are used for resources
4
+ module Source
5
+ # This module implements helpers that are used for resources
6
+ module Install
7
+ VERSION = '0.0.0'
8
+
9
+ # Hooks for install
10
+
11
+ def base_name(_new_resource)
12
+ raise NotImplementedError('Client must define base application name e.g. "Python". \
13
+ This is frequently used to build e.g. archive file name and will be used to define default directories.')
14
+ end
15
+
16
+ def default_version(_new_resource)
17
+ raise NotImplementedError('Client must provide a default version string e.g. "1.1.4".')
18
+ end
19
+
20
+ def archive_file_name(_new_resource)
21
+ # noinspection RubyExpressionInStringInspection
22
+ raise NotImplementedError('Client must indicate the filename of the what the archive file \
23
+ that will be downloaded, e.g. "#{base_name(new_resource)}-#{new_resource.version}.tar.gz"')
24
+ end
25
+
26
+ def download_base_url(_new_resource)
27
+ raise NotImplementedError('Client must indicate the URL at which the archive file is located, \
28
+ e.g. "https://www.oss-project.org/source"')
29
+ end
30
+
31
+ def archive_root_directory(_new_resource)
32
+ # noinspection RubyExpressionInStringInspection
33
+ raise NotImplementedError('Client must indicate the directory created by extraction, \
34
+ e.g. "#{base_name(new_resource)}-#{new_resource.version}". This will be used as the build directory.')
35
+ end
36
+
37
+ def extract_creates_file(_new_resource)
38
+ raise NotImplementedError('Client must indicate a relative path to a file that is created by extraction, \
39
+ e.g. "README.md"')
40
+ end
41
+
42
+ def configuration_command(_new_resource)
43
+ raise NotImplementedError('Client must define the configuration command to be run before build, \
44
+ e.g. "./config shared --prefix..."')
45
+ end
46
+
47
+ def install_creates_file(_new_resource)
48
+ raise NotImplementedError('Client must indicate a relative path to a file that is created by installation, \
49
+ e.g. "bin/my_app"')
50
+ end
51
+
52
+ def install_command(_new_resource)
53
+ raise NotImplementedError('Client must define the command for installation, e.g. "make install"')
54
+ end
55
+
56
+ def post_install_logic(_new_resource)
57
+ raise NotImplementedError('Client must define logic to run after installation, for example to creating symlinks')
58
+ end
59
+
60
+ # Common install code
61
+
62
+ def ensure_version(new_resource)
63
+ return if new_resource.version
64
+
65
+ new_resource.version = default_version(new_resource)
66
+ end
67
+
68
+ def download_url(new_resource)
69
+ url = download_base_url(new_resource)
70
+ url += '/' unless url.match?(%r{/$})
71
+ url += archive_file_name(new_resource)
72
+ return url
73
+ end
74
+
75
+ def create_default_directories
76
+ directory '/var/chef' do
77
+ mode 0o755
78
+ owner 'root'
79
+ group 'root'
80
+ end
81
+ directory '/var/chef/cache' do
82
+ mode 0o755
83
+ owner 'root'
84
+ group 'root'
85
+ end
86
+ end
87
+
88
+ def path_to_download_directory(new_resource)
89
+ return new_resource.download_directory if new_resource.download_directory
90
+
91
+ create_default_directories
92
+ return '/var/chef/cache'
93
+ end
94
+
95
+ def path_to_download_file(new_resource)
96
+ directory = path_to_download_directory(new_resource)
97
+ file = File.join(directory, archive_file_name(new_resource))
98
+ return file
99
+ end
100
+
101
+ def download_archive(new_resource)
102
+ download_file = path_to_download_file(new_resource)
103
+ url = download_url(new_resource)
104
+ remote_file download_file do
105
+ source url
106
+ owner new_resource.owner
107
+ group new_resource.group
108
+ end
109
+ return download_file
110
+ end
111
+
112
+ def path_to_build_directory(new_resource)
113
+ base = archive_root_directory(new_resource)
114
+ return File.join(new_resource.build_directory, base) if new_resource.build_directory
115
+
116
+ create_default_directories
117
+ return File.join('/var/chef/cache', base)
118
+ end
119
+
120
+ def clear_source_directory(build_directory, new_resource)
121
+ dir = build_directory
122
+ bash 'Clear Archive' do
123
+ code "rm -rf #{dir}\nmkdir #{dir}\nchown #{new_resource.owner}:#{new_resource.group} #{dir}"
124
+ # Run as root so we blow it away if the owner changes
125
+ action :nothing
126
+ subscribes :run, 'checksum_file[Download Checksum]', :immediate
127
+ end
128
+ end
129
+
130
+ def manage_source_directory(download_file, build_directory, new_resource)
131
+ checksum_file 'Download Checksum' do
132
+ source_path download_file
133
+ target_path "/var/chef/cache/#{base_name(new_resource).downcase}-#{new_resource.version}-dl-checksum"
134
+ end
135
+ clear_source_directory(build_directory, new_resource)
136
+ end
137
+
138
+ def extract_command(filename)
139
+ return 'unzip -q' if filename.match?(/\.zip/)
140
+
141
+ return 'tar xzf' if filename.match?(/\.(:?tar\.gz|tgz)/)
142
+
143
+ raise "Archive not supported: #{filename}"
144
+ end
145
+
146
+ def code_for_extraction(download_file, build_directory, new_resource)
147
+ code = <<~CODE
148
+ #{extract_command(download_file)} #{download_file}
149
+ chown -R #{new_resource.owner}:#{new_resource.group} #{build_directory}
150
+ CODE
151
+ return code
152
+ end
153
+
154
+ def extract_download(download_file, build_directory, new_resource)
155
+ # Built-in archive_file requires Chef 15 and poise_archive is failing to exhibit idempotence on zip files
156
+ parent = File.dirname(build_directory)
157
+ code = code_for_extraction(download_file, build_directory, new_resource)
158
+ bash 'Extract Archive' do
159
+ code code
160
+ cwd parent
161
+ # Run as root in case it is installing in directory without write access
162
+ creates File.join(build_directory, extract_creates_file(new_resource))
163
+ end
164
+ end
165
+
166
+ def extract_archive(build_directory, new_resource)
167
+ download_file = download_archive(new_resource)
168
+ manage_source_directory(download_file, build_directory, new_resource)
169
+ extract_download(download_file, build_directory, new_resource)
170
+ end
171
+
172
+ def default_install_directory(new_resource)
173
+ return "/opt/#{base_name(new_resource).downcase}/#{new_resource.version}"
174
+ end
175
+
176
+ def create_opt_directories(new_resource)
177
+ directory "/opt/#{base_name(new_resource).downcase}" do
178
+ mode 0o755
179
+ owner 'root'
180
+ group 'root'
181
+ end
182
+ directory default_install_directory(new_resource) do
183
+ mode 0o755
184
+ owner 'root'
185
+ group 'root'
186
+ end
187
+ end
188
+
189
+ def ensure_install_directory(new_resource)
190
+ return if new_resource.install_directory
191
+
192
+ create_opt_directories(new_resource)
193
+ new_resource.install_directory = default_install_directory(new_resource)
194
+ end
195
+
196
+ def save_config(code, new_resource)
197
+ file 'Config File' do
198
+ path "/var/chef/cache/#{base_name(new_resource).downcase}-#{new_resource.version}-config"
199
+ content code
200
+ mode 0o644
201
+ owner 'root'
202
+ group 'root'
203
+ end
204
+ end
205
+
206
+ def manage_make_file(build_directory, code, new_resource)
207
+ save_config(code, new_resource)
208
+ makefile = File.join(build_directory, 'Makefile')
209
+ file makefile do
210
+ action :nothing
211
+ subscribes :delete, 'file[Config File]', :immediate
212
+ end
213
+ return makefile
214
+ end
215
+
216
+ def configure_build(build_directory, new_resource)
217
+ code = configuration_command(new_resource)
218
+ makefile = manage_make_file(build_directory, code, new_resource)
219
+ bash 'Configure Build' do
220
+ code code
221
+ cwd build_directory
222
+ user new_resource.owner
223
+ group new_resource.group
224
+ creates makefile
225
+ end
226
+ end
227
+
228
+ def check_build_directory(build_directory, new_resource)
229
+ checksum_file 'Source Checksum' do
230
+ source_path build_directory
231
+ target_path "/var/chef/cache/#{base_name(new_resource).downcase}-#{new_resource.version}-src-checksum"
232
+ end
233
+ end
234
+
235
+ def manage_bin_file(bin_file)
236
+ file bin_file do
237
+ action :nothing
238
+ manage_symlink_source false
239
+ subscribes :delete, 'checksum_file[Source Checksum]', :immediate
240
+ end
241
+ end
242
+
243
+ def execute_build(build_directory, bin_file, new_resource)
244
+ bash 'Compile' do
245
+ code 'make'
246
+ cwd build_directory
247
+ user new_resource.owner
248
+ group new_resource.group
249
+ creates bin_file
250
+ end
251
+ end
252
+
253
+ def execute_install(build_directory, bin_file, new_resource)
254
+ bash 'Install' do
255
+ code install_command(new_resource)
256
+ cwd build_directory
257
+ # Run as root in case it is installing in directory without write access
258
+ creates bin_file
259
+ end
260
+ end
261
+
262
+ def recurse_command(path)
263
+ return ' -R' if File.directory?(path)
264
+
265
+ return ''
266
+ end
267
+
268
+ def command_for_file(filename, new_resource)
269
+ path = File.join(new_resource.install_directory, filename)
270
+ recurse = recurse_command(path)
271
+ return "\nchown#{recurse} #{new_resource.owner}:#{new_resource.group} #{path}"
272
+ end
273
+
274
+ def iterate_install_directory(new_resource)
275
+ command = ''
276
+ Dir.foreach(new_resource.install_directory) do |filename|
277
+ next if ['.', '..'].include?(filename)
278
+
279
+ command += command_for_file(filename, new_resource)
280
+ end
281
+ return command
282
+ end
283
+
284
+ def build_permission_command(new_resource)
285
+ ruby_block 'Build Children' do
286
+ block do
287
+ files = iterate_install_directory(new_resource)
288
+ node.run_state['build_permission_command'] = files
289
+ end
290
+ action :nothing
291
+ subscribes :run, 'bash[Install]', :immediate
292
+ end
293
+ end
294
+
295
+ # Some install scripts create artifacts in the source directory
296
+ def set_src_permissions(build_directory, new_resource)
297
+ bash 'Set Config Permissions' do
298
+ code "chown -R #{new_resource.owner}:#{new_resource.group} #{build_directory}"
299
+ action :nothing
300
+ subscribes :run, 'bash[Install]', :immediate
301
+ end
302
+ end
303
+
304
+ def set_install_permissions(build_directory, new_resource)
305
+ build_permission_command(new_resource)
306
+ bash 'Change Install Permissions' do
307
+ code(lazy { node.run_state['build_permission_command'] })
308
+ cwd new_resource.install_directory
309
+ action :nothing
310
+ subscribes :run, 'bash[Install]', :immediate
311
+ end
312
+ set_src_permissions(build_directory, new_resource)
313
+ end
314
+
315
+ def make_build(build_directory, bin_file, new_resource)
316
+ execute_build(build_directory, bin_file, new_resource)
317
+ execute_install(build_directory, bin_file, new_resource)
318
+ set_install_permissions(build_directory, new_resource)
319
+ end
320
+
321
+ def compile_and_install(build_directory, new_resource)
322
+ check_build_directory(build_directory, new_resource)
323
+ bin_file = File.join(new_resource.install_directory, install_creates_file(new_resource))
324
+ manage_bin_file(bin_file)
325
+ make_build(build_directory, bin_file, new_resource)
326
+ end
327
+
328
+ def build_binary(build_directory, new_resource)
329
+ ensure_install_directory(new_resource)
330
+ configure_build(build_directory, new_resource)
331
+ compile_and_install(build_directory, new_resource)
332
+ post_install_logic(new_resource)
333
+ end
334
+
335
+ def create_install(new_resource)
336
+ ensure_version(new_resource)
337
+ build_directory = path_to_build_directory(new_resource)
338
+ extract_archive(build_directory, new_resource)
339
+ build_binary(build_directory, new_resource)
340
+ end
341
+ end
342
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: source_install
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - UA OIT Systems Engineering
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-08-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: chefspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: kitchen-vagrant
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-kitchen
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Logic for downloading, configuring, compiling, and installing packages
70
+ from source
71
+ email:
72
+ - ua-oit-se@alaska.edu
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - LICENSE
78
+ - lib/source_install.rb
79
+ homepage: https://github.com/ualaska-it/source_install
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 2.4.0
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubygems_version: 3.0.3
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: A gem for common installer logic
102
+ test_files: []