Module: Source::Install

Defined in:
lib/source_install.rb

Overview

This module implements helpers that are used for resources

Constant Summary collapse

VERSION =
'1.1.1'

Instance Method Summary collapse

Instance Method Details

#archive_file_name(_new_resource) ⇒ Object



20
21
22
23
24
# File 'lib/source_install.rb', line 20

def archive_file_name(_new_resource)
  # noinspection RubyExpressionInStringInspection
  raise NotImplementedError('Client must indicate the filename of the what the archive file \
that will be downloaded, e.g. "#{base_name(new_resource)}-#{new_resource.version}.tar.gz"')
end

#archive_root_directory(_new_resource) ⇒ Object



31
32
33
34
35
# File 'lib/source_install.rb', line 31

def archive_root_directory(_new_resource)
  # noinspection RubyExpressionInStringInspection
  raise NotImplementedError('Client must indicate the directory created by extraction, \
e.g. "#{base_name(new_resource)}-#{new_resource.version}". This will be used as the build directory.')
end

#base_name(_new_resource) ⇒ Object

Hooks for install



11
12
13
14
# File 'lib/source_install.rb', line 11

def base_name(_new_resource)
  raise NotImplementedError('Client must define base application name e.g. "Python". \
This is frequently used to build e.g. archive file name and will be used to define default directories.')
end

#build_binary(build_directory, new_resource) ⇒ Object



347
348
349
350
351
352
# File 'lib/source_install.rb', line 347

def build_binary(build_directory, new_resource)
  ensure_install_directory(new_resource)
  configure_build(build_directory, new_resource)
  compile_and_install(build_directory, new_resource)
  post_install_logic(new_resource)
end

#build_command(_new_resource) ⇒ Object

Optional hooks for install



54
55
56
# File 'lib/source_install.rb', line 54

def build_command(_new_resource)
  return 'make'
end

#build_permission_command(new_resource) ⇒ Object



303
304
305
306
307
308
309
310
311
312
# File 'lib/source_install.rb', line 303

def build_permission_command(new_resource)
  ruby_block 'Build Children' do
    block do
      files = iterate_install_directory(new_resource)
      node.run_state['build_permission_command'] = files
    end
    action :nothing
    subscribes :run, 'bash[Install]', :immediate
  end
end

#check_build_directory(build_directory, new_resource) ⇒ Object



244
245
246
247
248
249
250
251
252
# File 'lib/source_install.rb', line 244

def check_build_directory(build_directory, new_resource)
  creates_file = config_creates_file(new_resource)
  checksum_file 'Source Checksum' do
    source_path File.join(build_directory, creates_file) if creates_file
    source_path build_directory unless creates_file
    target_path "/var/chef/cache/#{base_name(new_resource).downcase}-#{new_resource.version}-src-checksum"
     false
  end
end

#clear_source_directory(build_directory, new_resource) ⇒ Object



135
136
137
138
139
140
141
142
143
# File 'lib/source_install.rb', line 135

def clear_source_directory(build_directory, new_resource)
  dir = build_directory
  bash 'Clear Archive' do
    code "rm -rf #{dir}\nmkdir #{dir}\nchown #{new_resource.owner}:#{new_resource.group} #{dir}"
    # Run as root so we blow it away if the owner changes
    action :nothing
    subscribes :run, 'checksum_file[Download Checksum]', :immediate
  end
end

#code_for_extraction(download_file, build_directory, new_resource) ⇒ Object



162
163
164
165
166
167
168
# File 'lib/source_install.rb', line 162

def code_for_extraction(download_file, build_directory, new_resource)
  code = <<~CODE
    #{extract_command(download_file)} #{download_file}
    chown -R #{new_resource.owner}:#{new_resource.group} #{build_directory}
  CODE
  return code
end

#command_for_file(filename, new_resource) ⇒ Object



287
288
289
290
291
# File 'lib/source_install.rb', line 287

def command_for_file(filename, new_resource)
  path = File.join(new_resource.install_directory, filename)
  recurse = recurse_command(path)
  return "\nchown#{recurse} #{new_resource.owner}:#{new_resource.group} #{path}"
end

#compile_and_install(build_directory, new_resource) ⇒ Object



340
341
342
343
344
345
# File 'lib/source_install.rb', line 340

def compile_and_install(build_directory, new_resource)
  check_build_directory(build_directory, new_resource)
  bin_file = File.join(new_resource.install_directory, install_creates_file(new_resource))
  manage_bin_file(bin_file)
  make_build(build_directory, bin_file, new_resource)
end

#config_creates_file(_new_resource) ⇒ Object



67
68
69
70
71
72
73
# File 'lib/source_install.rb', line 67

def config_creates_file(_new_resource)
  # A relative path to a file created by configuration
  # If the client defines a config_creates_file, then the content of that file will be used to signal rebuild
  # Otherwise, a checksum is taken of the entire build directory
  # A file is less robust for signaling rebuild when config changes, but cleaner for nasty in-source builds
  return nil
end

#configuration_command(_new_resource) ⇒ Object



42
43
44
45
# File 'lib/source_install.rb', line 42

def configuration_command(_new_resource)
  raise NotImplementedError('Client must define the configuration command to be run before build, \
e.g. "./config shared --prefix..."')
end

#configure_build(build_directory, new_resource) ⇒ Object



232
233
234
235
236
237
238
239
240
241
242
# File 'lib/source_install.rb', line 232

def configure_build(build_directory, new_resource)
  code = configuration_command(new_resource)
  makefile = manage_make_file(build_directory, code, new_resource)
  bash 'Configure Build' do
    code code
    cwd build_directory
    user new_resource.owner
    group new_resource.group
    creates makefile
  end
end

#create_default_directoriesObject



90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/source_install.rb', line 90

def create_default_directories
  directory '/var/chef' do
    mode 0o755
    owner 'root'
    group 'root'
  end
  directory '/var/chef/cache' do
    mode 0o755
    owner 'root'
    group 'root'
  end
end

#create_install(new_resource) ⇒ Object



354
355
356
357
358
359
# File 'lib/source_install.rb', line 354

def create_install(new_resource)
  ensure_version(new_resource)
  build_directory = path_to_build_directory(new_resource)
  extract_archive(build_directory, new_resource)
  build_binary(build_directory, new_resource)
end

#create_opt_directories(new_resource) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/source_install.rb', line 192

def create_opt_directories(new_resource)
  directory "/opt/#{base_name(new_resource).downcase}" do
    mode 0o755
    owner 'root'
    group 'root'
  end
  directory default_install_directory(new_resource) do
    mode 0o755
    owner 'root'
    group 'root'
  end
end

#default_install_directory(new_resource) ⇒ Object



188
189
190
# File 'lib/source_install.rb', line 188

def default_install_directory(new_resource)
  return "/opt/#{base_name(new_resource).downcase}/#{new_resource.version}"
end

#default_version(_new_resource) ⇒ Object



16
17
18
# File 'lib/source_install.rb', line 16

def default_version(_new_resource)
  raise NotImplementedError('Client must provide a default version string e.g. "1.1.4".')
end

#download_archive(new_resource) ⇒ Object



116
117
118
119
120
121
122
123
124
125
# File 'lib/source_install.rb', line 116

def download_archive(new_resource)
  download_file = path_to_download_file(new_resource)
  url = download_url(new_resource)
  remote_file download_file do
    source url
    owner new_resource.owner
    group new_resource.group
  end
  return download_file
end

#download_base_url(_new_resource) ⇒ Object



26
27
28
29
# File 'lib/source_install.rb', line 26

def download_base_url(_new_resource)
  raise NotImplementedError('Client must indicate the URL at which the archive file is located, \
e.g. "https://www.oss-project.org/source"')
end

#download_url(new_resource) ⇒ Object



83
84
85
86
87
88
# File 'lib/source_install.rb', line 83

def download_url(new_resource)
  url = download_base_url(new_resource)
  url += '/' unless url.match?(%r{/$})
  url += archive_file_name(new_resource)
  return url
end

#ensure_install_directory(new_resource) ⇒ Object



205
206
207
208
209
210
# File 'lib/source_install.rb', line 205

def ensure_install_directory(new_resource)
  return if new_resource.install_directory

  create_opt_directories(new_resource)
  new_resource.install_directory = default_install_directory(new_resource)
end

#ensure_version(new_resource) ⇒ Object

Common install code; the hooks are intended to save clients the effort of implementing anything below



77
78
79
80
81
# File 'lib/source_install.rb', line 77

def ensure_version(new_resource)
  return if new_resource.version

  new_resource.version = default_version(new_resource)
end

#execute_build(build_directory, bin_file, new_resource) ⇒ Object



262
263
264
265
266
267
268
269
270
# File 'lib/source_install.rb', line 262

def execute_build(build_directory, bin_file, new_resource)
  bash 'Compile' do
    code build_command(new_resource)
    cwd build_directory
    user new_resource.owner
    group new_resource.group
    creates bin_file
  end
end

#execute_install(build_directory, bin_file, new_resource) ⇒ Object



272
273
274
275
276
277
278
279
# File 'lib/source_install.rb', line 272

def execute_install(build_directory, bin_file, new_resource)
  bash 'Install' do
    code install_command(new_resource)
    cwd build_directory
    # Run as root in case it is installing in directory without write access
    creates bin_file
  end
end

#extract_archive(build_directory, new_resource) ⇒ Object



182
183
184
185
186
# File 'lib/source_install.rb', line 182

def extract_archive(build_directory, new_resource)
  download_file = download_archive(new_resource)
  manage_source_directory(download_file, build_directory, new_resource)
  extract_download(download_file, build_directory, new_resource)
end

#extract_command(filename) ⇒ Object



154
155
156
157
158
159
160
# File 'lib/source_install.rb', line 154

def extract_command(filename)
  return 'unzip -q' if filename.match?(/\.zip/)

  return 'tar xzf' if filename.match?(/\.(:?tar\.gz|tgz)/)

  raise "Archive not supported: #{filename}"
end

#extract_creates_file(_new_resource) ⇒ Object



37
38
39
40
# File 'lib/source_install.rb', line 37

def extract_creates_file(_new_resource)
  raise NotImplementedError('Client must indicate a relative path to a file that is created by extraction, \
e.g. "README.md"')
end

#extract_download(download_file, build_directory, new_resource) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
# File 'lib/source_install.rb', line 170

def extract_download(download_file, build_directory, new_resource)
  # Built-in archive_file requires Chef 15 and poise_archive is failing to exhibit idempotence on zip files
  parent = File.dirname(build_directory)
  code = code_for_extraction(download_file, build_directory, new_resource)
  bash 'Extract Archive' do
    code code
    cwd parent
    # Run as root in case it is installing in directory without write access
    creates File.join(build_directory, extract_creates_file(new_resource))
  end
end

#install_command(_new_resource) ⇒ Object



58
59
60
61
# File 'lib/source_install.rb', line 58

def install_command(_new_resource)
  # This is probably the most common command, another option is 'make altinstall'
  return 'make install'
end

#install_creates_file(_new_resource) ⇒ Object



47
48
49
50
# File 'lib/source_install.rb', line 47

def install_creates_file(_new_resource)
  raise NotImplementedError('Client must indicate a relative path to a file that is created by installation, \
e.g. "bin/my_app"')
end

#iterate_install_directory(new_resource) ⇒ Object



293
294
295
296
297
298
299
300
301
# File 'lib/source_install.rb', line 293

def iterate_install_directory(new_resource)
  command = ''
  Dir.foreach(new_resource.install_directory) do |filename|
    next if ['.', '..'].include?(filename)

    command += command_for_file(filename, new_resource)
  end
  return command
end

#make_build(build_directory, bin_file, new_resource) ⇒ Object



334
335
336
337
338
# File 'lib/source_install.rb', line 334

def make_build(build_directory, bin_file, new_resource)
  execute_build(build_directory, bin_file, new_resource)
  execute_install(build_directory, bin_file, new_resource)
  set_install_permissions(build_directory, new_resource)
end

#manage_bin_file(bin_file) ⇒ Object



254
255
256
257
258
259
260
# File 'lib/source_install.rb', line 254

def manage_bin_file(bin_file)
  file bin_file do
    action :nothing
    manage_symlink_source false
    subscribes :delete, 'checksum_file[Source Checksum]', :immediate
  end
end

#manage_make_file(build_directory, code, new_resource) ⇒ Object



222
223
224
225
226
227
228
229
230
# File 'lib/source_install.rb', line 222

def manage_make_file(build_directory, code, new_resource)
  save_config(code, new_resource)
  makefile = File.join(build_directory, 'Makefile')
  file makefile do
    action :nothing
    subscribes :delete, 'file[Config File]', :immediate
  end
  return makefile
end

#manage_source_directory(download_file, build_directory, new_resource) ⇒ Object



145
146
147
148
149
150
151
152
# File 'lib/source_install.rb', line 145

def manage_source_directory(download_file, build_directory, new_resource)
  checksum_file 'Download Checksum' do
    source_path download_file
    target_path "/var/chef/cache/#{base_name(new_resource).downcase}-#{new_resource.version}-dl-checksum"
     false
  end
  clear_source_directory(build_directory, new_resource)
end

#path_to_build_directory(new_resource) ⇒ Object



127
128
129
130
131
132
133
# File 'lib/source_install.rb', line 127

def path_to_build_directory(new_resource)
  base = archive_root_directory(new_resource)
  return File.join(new_resource.build_directory, base) if new_resource.build_directory

  create_default_directories
  return File.join('/var/chef/cache', base)
end

#path_to_download_directory(new_resource) ⇒ Object



103
104
105
106
107
108
# File 'lib/source_install.rb', line 103

def path_to_download_directory(new_resource)
  return new_resource.download_directory if new_resource.download_directory

  create_default_directories
  return '/var/chef/cache'
end

#path_to_download_file(new_resource) ⇒ Object



110
111
112
113
114
# File 'lib/source_install.rb', line 110

def path_to_download_file(new_resource)
  directory = path_to_download_directory(new_resource)
  file = File.join(directory, archive_file_name(new_resource))
  return file
end

#post_install_logic(_new_resource) ⇒ Object



63
64
65
# File 'lib/source_install.rb', line 63

def post_install_logic(_new_resource)
  # Client may define logic to run after installation, for example for creating symlinks
end

#recurse_command(path) ⇒ Object



281
282
283
284
285
# File 'lib/source_install.rb', line 281

def recurse_command(path)
  return ' -R' if File.directory?(path)

  return ''
end

#save_config(code, new_resource) ⇒ Object



212
213
214
215
216
217
218
219
220
# File 'lib/source_install.rb', line 212

def save_config(code, new_resource)
  file 'Config File' do
    path "/var/chef/cache/#{base_name(new_resource).downcase}-#{new_resource.version}-config"
    content code
    mode 0o644
    owner 'root'
    group 'root'
  end
end

#set_install_permissions(build_directory, new_resource) ⇒ Object



323
324
325
326
327
328
329
330
331
332
# File 'lib/source_install.rb', line 323

def set_install_permissions(build_directory, new_resource)
  build_permission_command(new_resource)
  bash 'Change Install Permissions' do
    code(lazy { node.run_state['build_permission_command'] })
    cwd new_resource.install_directory
    action :nothing
    subscribes :run, 'bash[Install]', :immediate
  end
  set_src_permissions(build_directory, new_resource)
end

#set_src_permissions(build_directory, new_resource) ⇒ Object

Some install scripts create artifacts in the source directory



315
316
317
318
319
320
321
# File 'lib/source_install.rb', line 315

def set_src_permissions(build_directory, new_resource)
  bash 'Set Config Permissions' do
    code "chown -R #{new_resource.owner}:#{new_resource.group} #{build_directory}"
    action :nothing
    subscribes :run, 'bash[Install]', :immediate
  end
end