class SIB::Image

SIB::Image represents a docker image

Attributes

image[RW]
repo[RW]

Public Class Methods

build(repo:, build_dir:, dockerfile: 'Dockerfile', labels: {}, squash: false) click to toggle source

TODO: Fix rubocop:disable Metrics/MethodLength

# File lib/service_image_builder/image.rb, line 14
def self.build(repo:, build_dir:, dockerfile: 'Dockerfile', labels: {}, squash: false)
  build_dir ||= Dir.pwd
  build_args = {
    'dockerfile' => dockerfile,
    'labels' => labels_to_json(labels),
    'squash' => squash
  }
  SIB.log.info("Building #{repo} from #{Pathname.new(build_dir).join(dockerfile)}")

  # TODO: maybe have parse_api_response do the logging & just raise an error
  # when one gets detected
  image = Docker::Image.build_from_dir(build_dir, build_args) do |resp|
    parse_api_response(resp).each do |r|
      SIB.log.info(r['stream'].rstrip) if r.key?('stream')
    end
  end

  new(repo: repo, image: image)
end
import(repo:, tag:) click to toggle source

rubocop:enable Metrics/MethodLength

# File lib/service_image_builder/image.rb, line 35
def self.import(repo:, tag:)
  new(repo: repo, image: Docker::Image.create('fromImage' => "#{repo}:#{tag}"))
rescue Docker::Error::NotFoundError
  nil
end
labels_to_json(labels) click to toggle source
# File lib/service_image_builder/image.rb, line 106
def labels_to_json(labels)
  Oj.dump(labels || {})
rescue Oj::Error => e
  raise ImageError, "Couldn't convert labels to json: #{e.message}"
end
new(repo:, image:) click to toggle source
# File lib/service_image_builder/image.rb, line 41
def initialize(repo:, image:)
  @image = image
  @repo = repo
end
parse_api_response(response) click to toggle source
# File lib/service_image_builder/image.rb, line 112
def parse_api_response(response)
  response.split("\r\n").map { |r| Oj.load(r) }
rescue Oj::Error => e
  SIB.log.error("Couldn't JSON parse api response: #{e.message}")
  SIB.log.info("Unparseable message: #{response}")
end

Public Instance Methods

<=>(other) click to toggle source

Image#<=> is a comparison operator implementation for SIB::Image. If the factor_hash are the same, they're equal. Otherwise, the latest timestamp wins.

@param other; the SIB::Image to compare against

# File lib/service_image_builder/image.rb, line 51
def <=>(other)
  return 0 if factor_hash == other.factor_hash

  timestamp.compare_with_coercion(other.timestamp) # <=> with coercion
end
commit_id() click to toggle source
# File lib/service_image_builder/image.rb, line 57
def commit_id
  @commit_id ||= labels.fetch('systems.slush.commit-id', nil)
end
factor_hash() click to toggle source
# File lib/service_image_builder/image.rb, line 61
def factor_hash
  @factor_hash ||= Digest::SHA2.new(256).tap do |hash|
    hash << commit_id
    hash << package_manifest
  end.hexdigest[0..15]
end
labels() click to toggle source
# File lib/service_image_builder/image.rb, line 68
def labels
  @labels ||= image.json['Config']['Labels']
end
package_manifest() click to toggle source

Image#package_manifest creates a container from @image and generates the package-manifest from it TODO extract the entire factor system out into its own object After that, remove the `Metrics/MethodLength` cop override

# File lib/service_image_builder/image.rb, line 76
def package_manifest
  @package_manifest ||= String.new.tap do |manifest|
    container = Docker::Container.create(
      'Cmd' => ['/bin/bash', '-c',
                'dnf list installed | sha256sum | colrm 65 > /package-manifest'],
      'Image' => image.id,
      'User' => 'root'
    )
    container.tap(&:start).tap(&:wait)
    manifest << container.read_file('/package-manifest').chomp
    container.remove
  end
end
push_tag(tag:) click to toggle source
# File lib/service_image_builder/image.rb, line 90
def push_tag(tag:)
  image.tag(repo: repo, tag: tag)
  image.push(nil, tag: tag) do |msg|
    SIB::Image.parse_api_response(msg).each do |r|
      raise SIB::ImageError, r if r.key?('error')

      SIB.log.info(r)
    end
  end
end
timestamp() click to toggle source
# File lib/service_image_builder/image.rb, line 101
def timestamp
  @timestamp ||= Time.parse(labels.fetch('systems.slush.timestamp', nil))
end