Class: PicsolveDockerBuilder::Frame

Inherits:
Object
  • Object
show all
Includes:
Base
Defined in:
lib/picsolve_docker_builder/frame.rb

Overview

Docker image building template rubocop:disable Metrics/ClassLength

Direct Known Subclasses

NodeJs, Scala

Instance Method Summary collapse

Methods included from Base

#base_dir, #config, #config_file, #config_path, #config_paths, #read_config

Constructor Details

#initializeFrame

Returns a new instance of Frame.



14
15
16
17
18
19
20
# File 'lib/picsolve_docker_builder/frame.rb', line 14

def initialize
  # TODO: @christiansimon please report that upstream, second time
  # i am facing that bug
  Docker.options[:read_timeout] = 3600
  Excon.defaults[:read_timeout] = 3600
  config
end

Instance Method Details

#asset_buildObject



143
144
# File 'lib/picsolve_docker_builder/frame.rb', line 143

def asset_build
end

#asset_imageObject



387
388
389
# File 'lib/picsolve_docker_builder/frame.rb', line 387

def asset_image
  @asset_image ||= fetch_asset_image
end

#asset_image_buildObject



353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/picsolve_docker_builder/frame.rb', line 353

def asset_image_build
  tar_contents = {
    'Dockerfile' => asset_image_dockerfile.join("\n"),
    '.gitconfig' => [
      '[user]',
      'name = Jenkins London Picsolve',
      'email = [email protected]'
    ].join("\n")
  }

  tar_contents['ssh_known_hosts'] = ssh_known_hosts if ssh_auth_forwarding?

  tar = StringIO.new Docker::Util.create_tar(tar_contents)
  begin
    Docker::Image.build_from_tar(tar) do |stream|
      s = JSON.parse(stream)['stream']
      log.debug s.strip unless s.nil?
    end
  rescue StandardError => e
    log.fatal "asset building failed: #{e}"
    exit 1
  end
end

#asset_image_dockerfileObject



339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/picsolve_docker_builder/frame.rb', line 339

def asset_image_dockerfile
  [
    "FROM #{image_name}",
    'MAINTAINER Picsolve Onlineops <[email protected]>',
    dockerfile_hooks_asset_build_early,
    "RUN useradd -m -d #{build_user_home} \\",
    " -u #{build_user_uid} #{build_user}",
    "ADD .gitconfig #{File.join(build_user_home, '.gitconfig')}",
    ssh_auth_forwarding_dockerfile,
    "RUN chown -cR #{build_user} #{build_user_home}",
    dockerfile_hooks_asset_build_late
  ]
end

#buildObject



146
147
148
149
150
151
152
153
# File 'lib/picsolve_docker_builder/frame.rb', line 146

def build
  asset_build
  if dest_image_name.nil?
    log.info 'Skip building docker image as no dest_image is set'
    return
  end
  docker_build
end

#build_dirObject



236
237
238
# File 'lib/picsolve_docker_builder/frame.rb', line 236

def build_dir
  '/_build'
end

#build_modeObject



46
47
48
49
50
51
52
# File 'lib/picsolve_docker_builder/frame.rb', line 46

def build_mode
  return :template unless image_name.nil?

  return :dockerfile if dockerfile_exists?

  log.fatal 'No image_name configured and no Dockerfile present'
end

#build_userObject



232
233
234
# File 'lib/picsolve_docker_builder/frame.rb', line 232

def build_user
  'build'
end

#build_user_homeObject



228
229
230
# File 'lib/picsolve_docker_builder/frame.rb', line 228

def build_user_home
  '/home/build'
end

#build_user_uidObject



224
225
226
# File 'lib/picsolve_docker_builder/frame.rb', line 224

def build_user_uid
  Process.uid.to_s
end

#containerObject



240
241
242
# File 'lib/picsolve_docker_builder/frame.rb', line 240

def container
  @container ||= create_container
end

#create_containerObject



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/picsolve_docker_builder/frame.rb', line 267

def create_container
  command = ['/bin/sleep', '3600']
  c = Docker::Container.create(
    'Image'      => asset_image.id,
    'Cmd'        => command,
    'OpenStdin'  => false,
    'WorkingDir' => '/_build',
    'Env'        => environment
  )
  at_exit do
    stop
  end
  log.debug "created a new container image=#{image_name} " \
            "id=#{c.id} cmd=#{command}"
  c
end

#create_loggerObject



391
392
393
394
395
# File 'lib/picsolve_docker_builder/frame.rb', line 391

def create_logger
  log = Logger.new(STDOUT)
  log.level = Logger::DEBUG
  log
end

#default_configObject



22
23
24
# File 'lib/picsolve_docker_builder/frame.rb', line 22

def default_config
  { 'docker' => {} }
end

#dest_image_nameObject



284
285
286
# File 'lib/picsolve_docker_builder/frame.rb', line 284

def dest_image_name
  config['docker']['image_name']
end

#docker_buildObject



135
136
137
138
139
140
141
# File 'lib/picsolve_docker_builder/frame.rb', line 135

def docker_build
  dockerfile

  log.info "start docker image building with path #{base_dir}"

  @docker_build = docker_build_build
end

#docker_build_buildObject



125
126
127
128
129
130
131
132
133
# File 'lib/picsolve_docker_builder/frame.rb', line 125

def docker_build_build
  Docker::Image.build_from_dir(base_dir) do |stream|
    s = JSON.parse(stream)['stream']
    log.debug s.strip unless s.nil?
  end
rescue StandardError => e
  log.fatal "docker building failed: #{e}"
  exit 1
end

#dockerfileObject



159
160
161
162
163
164
165
166
167
168
# File 'lib/picsolve_docker_builder/frame.rb', line 159

def dockerfile
  return unless build_mode == :template

  File.open('Dockerfile', 'w') do |file|
    dockerfile_template.each_line do |line|
      log.debug "Dockerfile: #{line.strip}"
    end
    file.write(dockerfile_template)
  end
end

#dockerfile_exists?Boolean

Returns:

  • (Boolean)


170
171
172
# File 'lib/picsolve_docker_builder/frame.rb', line 170

def dockerfile_exists?
  File.exist? dockerfile_path
end

#dockerfile_hooks_asset_build_earlyObject



66
67
68
69
70
# File 'lib/picsolve_docker_builder/frame.rb', line 66

def dockerfile_hooks_asset_build_early
  config['docker']['dockerfile_hooks']['asset_build']['early']
rescue NoMethodError
  ''
end

#dockerfile_hooks_asset_build_lateObject



72
73
74
75
76
# File 'lib/picsolve_docker_builder/frame.rb', line 72

def dockerfile_hooks_asset_build_late
  config['docker']['dockerfile_hooks']['asset_build']['late']
rescue NoMethodError
  ''
end

#dockerfile_hooks_docker_build_earlyObject



54
55
56
57
58
# File 'lib/picsolve_docker_builder/frame.rb', line 54

def dockerfile_hooks_docker_build_early
  config['docker']['dockerfile_hooks']['docker_build']['early']
rescue NoMethodError
  ''
end

#dockerfile_hooks_docker_build_lateObject



60
61
62
63
64
# File 'lib/picsolve_docker_builder/frame.rb', line 60

def dockerfile_hooks_docker_build_late
  config['docker']['dockerfile_hooks']['docker_build']['late']
rescue NoMethodError
  ''
end

#dockerfile_pathObject



155
156
157
# File 'lib/picsolve_docker_builder/frame.rb', line 155

def dockerfile_path
  File.join(base_dir, 'Dockerfile')
end

#dockerfile_templateObject



174
175
176
# File 'lib/picsolve_docker_builder/frame.rb', line 174

def dockerfile_template
  fail NotImplementedError
end

#dockerignore_templateObject



178
179
180
# File 'lib/picsolve_docker_builder/frame.rb', line 178

def dockerignore_template
  fail NotImplementedError
end

#environmentObject



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/picsolve_docker_builder/frame.rb', line 244

def environment
  blacklist = %w(
    SSH_CLIENT
    SSH_CONNECTION
    LD_LIBRARY_PATH
    PATH
    NVM_DIR
    NVM_NODEJS_ORG_MIRROR)

  keys = ENV.keys
  keys = keys.reject do |key|
    blacklist.include? key
  end

  env = keys.map do |key|
    "#{key}=#{ENV[key]}"
  end

  env << "SSH_AUTH_SOCK=#{ssh_auth_forwarding_path}" if ssh_auth_forwarding?

  env
end

#execute(cmd) ⇒ Object



26
27
28
29
30
31
# File 'lib/picsolve_docker_builder/frame.rb', line 26

def execute(cmd)
  r = container.exec(cmd)
  fail "Execution of cmd=#{cmd} failed" unless r[2] == 0
  log.debug "executed container id=#{container.id} cmd=#{cmd} result=#{r}"
  r
end

#execute_attach(cmd) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/picsolve_docker_builder/frame.rb', line 33

def execute_attach(cmd)
  log.debug "execute and attach container id=#{container.id} cmd=#{cmd}"
  r = container.exec(
    cmd
  ) do |_stream, chunk|
    $stdout.write chunk
    $stdout.flush
  end
  fail "Execution of cmd=#{cmd} failed" unless r[2] == 0
  log.debug 'executed and attached container ' \
            "id=#{container.id} cmd=#{cmd} exitcode=#{r[2]}"
end

#fetch_asset_imageObject



377
378
379
380
381
382
383
384
385
# File 'lib/picsolve_docker_builder/frame.rb', line 377

def fetch_asset_image
  log.debug "pulling image '#{image_name}' from registry"
  Docker::Image.create(
    { 'fromImage' => image_name },
    PicsolveDockerBuilder::Helpers::Registry.creds
  )
  log.debug "building asset image from '#{image_name}'"
  asset_image_build
end

#image_nameObject



300
301
302
303
304
305
306
307
308
309
310
# File 'lib/picsolve_docker_builder/frame.rb', line 300

def image_name
  name = config['docker']['base_image']

  if name.match(/:[a-z0-9\-_]+$/)
    name
  else
    "#{name}:latest"
  end
rescue NoMethodError
  nil
end

#jenkins_build_numberObject



78
79
80
81
82
# File 'lib/picsolve_docker_builder/frame.rb', line 78

def jenkins_build_number
  n = ENV['BUILD_NUMBER']
  return nil if n.nil?
  n.to_i
end

#logObject



397
398
399
# File 'lib/picsolve_docker_builder/frame.rb', line 397

def log
  @logger ||= create_logger
end

#pushObject



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/picsolve_docker_builder/frame.rb', line 102

def push
  fail 'No image found to be pushed' if @docker_build.nil?
  tags.each do |tag|
    repotag = "#{dest_image_name}:#{tag}"
    log.info "pushing image with #{repotag}"
    @docker_build.push(
      PicsolveDockerBuilder::Helpers::Registry.creds,
      tag: tag
    ) do |resp|
      resp = JSON.parse resp

      if resp.key? 'errorDetail'
        message = "pushing image #{repotag} failed: #{resp['errorDetail']}"
        log.fatal message
        fail message
      end

      log.info "status pushing image #{repotag}: #{resp['status']}" \
        if resp.key? 'status'
    end
  end
end

#runtime_image_nameObject



288
289
290
291
292
293
294
295
296
297
298
# File 'lib/picsolve_docker_builder/frame.rb', line 288

def runtime_image_name
  name = config['docker']['runtime_image'] || image_name

  if name.match(/:[a-z0-9\-_]+$/)
    name
  else
    "#{name}:latest"
  end
rescue NoMethodError
  nil
end

#ssh_auth_forwarding?Boolean

Returns:

  • (Boolean)


312
313
314
315
# File 'lib/picsolve_docker_builder/frame.rb', line 312

def ssh_auth_forwarding?
  return true if config['docker']['ssh_auth_forwarding']
  false
end

#ssh_auth_forwarding_dockerfileObject



317
318
319
320
321
# File 'lib/picsolve_docker_builder/frame.rb', line 317

def ssh_auth_forwarding_dockerfile
  return "\n" unless ssh_auth_forwarding?
  # add ssh known hosts to the image if using forwards
  "ADD ssh_known_hosts #{File.join(build_user_home, '.ssh/known_hosts')}"
end

#ssh_auth_forwarding_pathObject



323
324
325
# File 'lib/picsolve_docker_builder/frame.rb', line 323

def ssh_auth_forwarding_path
  '/tmp/ssh_auth_sock/ssh_auth_sock'
end

#ssh_known_hostsObject



335
336
337
# File 'lib/picsolve_docker_builder/frame.rb', line 335

def ssh_known_hosts
  File.open(File.join(Dir.home, '.ssh/known_hosts')).read
end

#startObject



190
191
192
193
194
# File 'lib/picsolve_docker_builder/frame.rb', line 190

def start
  container.start('Binds' => volumes)

  log.debug "started container id=#{container.id} volumes=#{volumes}"
end

#stopObject



196
197
198
199
200
201
202
203
# File 'lib/picsolve_docker_builder/frame.rb', line 196

def stop
  return if @container.nil?
  container.stop
  log.debug "stopped container id=#{container.id}"
  container.remove
  log.debug "removed container id=#{container.id}"
  @container = nil
end

#tagObject



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

def tag
  fail 'No image found to tag' if @docker_build.nil?
  tags.each do |tag|
    log.info "tagging image with #{dest_image_name}:#{tag}"
    @docker_build.tag(
      repo: dest_image_name,
      tag: tag,
      force: true
    )
  end
end

#tagsObject



84
85
86
87
88
# File 'lib/picsolve_docker_builder/frame.rb', line 84

def tags
  t = ['latest']
  t << "jenkins-#{jenkins_build_number}" unless jenkins_build_number.nil?
  t
end

#validate_config(c) ⇒ Object



182
183
184
# File 'lib/picsolve_docker_builder/frame.rb', line 182

def validate_config(c)
  validate_config_docker(c)
end

#validate_config_docker(c) ⇒ Object



186
187
188
# File 'lib/picsolve_docker_builder/frame.rb', line 186

def validate_config_docker(c)
  c
end

#volume_ssh_auth_forwardingObject



327
328
329
330
331
332
333
# File 'lib/picsolve_docker_builder/frame.rb', line 327

def volume_ssh_auth_forwarding
  @ssh_auth_forwarding = Helpers::SshAuthForwarding.new
  [
    @ssh_auth_forwarding.dir,
    File.dirname(ssh_auth_forwarding_path)
  ]
end

#volume_workspaceObject



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

def volume_workspace
  [
    base_dir,
    build_dir
  ]
end

#volumesObject



218
219
220
221
222
# File 'lib/picsolve_docker_builder/frame.rb', line 218

def volumes
  volumes_array.map do |volume|
    volume.join ':'
  end
end

#volumes_arrayObject



212
213
214
215
216
# File 'lib/picsolve_docker_builder/frame.rb', line 212

def volumes_array
  volumes = [volume_workspace]
  volumes << volume_ssh_auth_forwarding if ssh_auth_forwarding?
  volumes
end