Class: SIB::Image

Inherits:
Object
  • Object
show all
Defined in:
lib/service_image_builder/image.rb

Overview

SIB::Image represents a docker image

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(repo:, image:) ⇒ Image

Returns a new instance of Image.



41
42
43
44
# File 'lib/service_image_builder/image.rb', line 41

def initialize(repo:, image:)
  @image = image
  @repo = repo
end

Instance Attribute Details

#imageObject

Returns the value of attribute image.



10
11
12
# File 'lib/service_image_builder/image.rb', line 10

def image
  @image
end

#repoObject

Returns the value of attribute repo.



10
11
12
# File 'lib/service_image_builder/image.rb', line 10

def repo
  @repo
end

Class Method Details

.build(repo:, build_dir:, dockerfile: 'Dockerfile', labels: {}, squash: false) ⇒ Object

TODO: Fix rubocop:disable Metrics/MethodLength



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 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:) ⇒ Object

rubocop:enable Metrics/MethodLength



35
36
37
38
39
# 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) ⇒ Object



106
107
108
109
110
# 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

.parse_api_response(response) ⇒ Object



112
113
114
115
116
117
# 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

Instance Method Details

#<=>(other) ⇒ Object

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

Parameters:

  • other;

    the SIB::Image to compare against



51
52
53
54
55
# 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_idObject



57
58
59
# File 'lib/service_image_builder/image.rb', line 57

def commit_id
  @commit_id ||= labels.fetch('systems.slush.commit-id', nil)
end

#factor_hashObject



61
62
63
64
65
66
# 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

#labelsObject



68
69
70
# File 'lib/service_image_builder/image.rb', line 68

def labels
  @labels ||= image.json['Config']['Labels']
end

#package_manifestObject

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



76
77
78
79
80
81
82
83
84
85
86
87
88
# 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:) ⇒ Object



90
91
92
93
94
95
96
97
98
99
# 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

#timestampObject



101
102
103
# File 'lib/service_image_builder/image.rb', line 101

def timestamp
  @timestamp ||= Time.parse(labels.fetch('systems.slush.timestamp', nil))
end