Module: PuppetDockerTools::Utilities

Defined in:
lib/puppet_docker_tools/utilities.rb

Class Method Summary collapse

Class Method Details

.current_git_sha(directory = '.') ⇒ Object

Get the current git sha for the specified directory

Parameters:

  • (defaults to: '.')


122
123
124
125
126
# File 'lib/puppet_docker_tools/utilities.rb', line 122

def current_git_sha(directory = '.')
  Dir.chdir directory do
    `git rev-parse HEAD`.strip
  end
end

.filter_build_args(build_args:, dockerfile:) ⇒ Object

Filter build args to only include ARGs listed in the dockerfile. This is meant for compatibility with old versions of docker.

Parameters:

  • hash of build args to filter

  • the dockerfile to look for ARGs in



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/puppet_docker_tools/utilities.rb', line 53

def filter_build_args(build_args: , dockerfile: )
  fail "File #{dockerfile} doesn't exist!" unless File.exist? dockerfile
  text = File.read(dockerfile)

  # Docker only supports passing a single ARG on each line, so
  # look for arg, and ignore any default settings since we only care
  # whether or not the key is available
  implemented_args = text.scan(/arg +([^\n=]+)/i).flatten

  # reject any entries for args that are not in the dockerfile
  build_args.reject { |k,v|
    if implemented_args.include?(k)
      false
    else
      puts "Rejecting `--build-arg #{k}` since that ARG isn't in the Dockerfile"
      true
    end
  }
end

.format_timestamp(timestamp) ⇒ Object

Convert timestamps from second since epoch to ISO 8601 timestamps. If the given timestamp is entirely numeric it will be converted to an ISO 8601 timestamp, if not the parameter will be returned as passed.

Parameters:

  • The timestamp to convert



133
134
135
136
137
138
# File 'lib/puppet_docker_tools/utilities.rb', line 133

def format_timestamp(timestamp)
  if "#{timestamp}" =~ /^\d+$/
    timestamp = Time.at(Integer(timestamp)).utc.iso8601
  end
  timestamp
end

.get_hadolint_command(file = '-') ⇒ Object

Generate the hadolint command that should be run. Hadolint is a linter for dockerfiles that also validates inline bash with shellcheck. For more info, see the github repo (github.com/hadolint/hadolint)

Parameters:

  • (defaults to: '-')

    Dockerfile to lint, defaults to stdin



191
192
193
194
195
196
197
198
199
200
201
# File 'lib/puppet_docker_tools/utilities.rb', line 191

def get_hadolint_command(file = '-')
  ignore_rules = [
    'DL3008',
    'DL3018',
    'DL4000',
    'DL4001',
  ]
  ignore_string = ignore_rules.map { |x| "--ignore #{x}" }.join(' ')

  "hadolint #{ignore_string} #{file}"
end

.get_value_from_base_image(value, namespace:, directory: '.', dockerfile: 'Dockerfile', dockerfile_contents: '') ⇒ Object

Get a value from a container’s base image

Parameters:

  • The value we want to get from this image’s base image, e.g. ‘version’

  • The namespace for the value, e.g. ‘org.label-schema’

  • (defaults to: '.')

    The directory containing the Dockerfile, defaults to $PWD

  • (defaults to: 'Dockerfile')

    The file name for your dockerfile, defaults to ‘Dockerfile’

  • (defaults to: '')

    A string containing the contents of the Dockerfile [optional]



226
227
228
229
# File 'lib/puppet_docker_tools/utilities.rb', line 226

def get_value_from_base_image(value, namespace:, directory: '.', dockerfile: 'Dockerfile', dockerfile_contents: '')
  base_image = get_value_from_dockerfile('from', directory: directory, dockerfile: dockerfile, dockerfile_contents: dockerfile_contents).split(':').first.split('/').last
  get_value_from_env(value, namespace: namespace, directory: "#{directory}/../#{base_image}", dockerfile: dockerfile)
end

.get_value_from_dockerfile(key, directory: '.', dockerfile: 'Dockerfile', dockerfile_contents: '') ⇒ Object

Get a value from a Dockerfile

Parameters:

  • The key to read from the Dockerfile, e.g. ‘from’

  • (defaults to: '.')

    The directory containing the Dockerfile, defaults to $PWD

  • (defaults to: 'Dockerfile')

    The file name for your dockerfile, defaults to ‘Dockerfile’

  • (defaults to: '')

    A string containing the contents of the Dockerfile [optional]



209
210
211
212
213
214
215
216
# File 'lib/puppet_docker_tools/utilities.rb', line 209

def get_value_from_dockerfile(key, directory: '.', dockerfile: 'Dockerfile', dockerfile_contents: '')
  if dockerfile_contents.empty?
    file = "#{directory}/#{dockerfile}"
    fail "File #{file} doesn't exist!" unless File.exist? file
    dockerfile_contents = File.read("#{file}")
  end
  dockerfile_contents[/^#{key.upcase} (.*$)/, 1]
end

.get_value_from_env(label, namespace: '', directory: '.', dockerfile: 'Dockerfile') ⇒ Object

Get a value from a Dockerfile. Extrapolates variables and variables set in the base docker image

Parameters:

  • The label containing the value you want to retrieve, e.g. ‘version’

  • (defaults to: '')

    The namespace for the label, e.g. ‘org.label-schema’

  • (defaults to: '.')

    The directory containing the Dockerfile, defaults to $PWD

  • (defaults to: 'Dockerfile')

    The file name for your dockerfile, defaults to ‘Dockerfile’



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/puppet_docker_tools/utilities.rb', line 93

def get_value_from_env(label, namespace: '', directory: '.', dockerfile: 'Dockerfile')
  file = "#{directory}/#{dockerfile}"
  fail "File #{file} doesn't exist!" unless File.exist? file
  text = File.read(file)

  value = text.scan(/#{Regexp.escape(namespace)}\.(.+)=(.+) \\?/).to_h[label]
  # tracking to make sure we aren't in an infinite variable loop
  checked_variables = []

  # expand out environment variables
  # This supports either label=$variable or label="$variable"
  while ! value.nil? && (value.start_with?('$') || value.start_with?('"$'))
    # if variable is quoted, get rid of leading and trailing quotes
    value.gsub!(/\A"|"\Z/, '')

    fail "Looks like there's an infinite loop with '#{value}'" if checked_variables.include?(value)

    checked_variables << value
    value = get_value_from_variable(value, directory: directory, dockerfile: dockerfile, dockerfile_contents: text)
  end
  # check in higher-level image if we didn't find it defined in this docker file
  value = get_value_from_base_image(label, namespace: namespace, directory: directory, dockerfile: dockerfile) if value.nil?
  # This gets rid of leading or trailing quotes
  value.gsub(/\A"|"\Z/, '')
end

.get_value_from_label(image, value:, namespace:) ⇒ Object

Get a value from the labels on a docker image

Parameters:

  • The docker image you want to get a value from, e.g. ‘puppet/puppetserver’

  • The value you want to get from the labels, e.g. ‘version’

  • The namespace for the value, e.g. ‘org.label-schema’



79
80
81
82
83
84
# File 'lib/puppet_docker_tools/utilities.rb', line 79

def get_value_from_label(image, value: , namespace: )
  labels = Docker::Image.get(image).json["Config"]["Labels"]
  labels["#{namespace}.#{value.tr('_', '-')}"]
rescue
  nil
end

.get_value_from_variable(variable, directory: '.', dockerfile: 'Dockerfile', dockerfile_contents: '') ⇒ Object

Get a value from a variable in a Dockerfile

Parameters:

  • The variable we want to look for in the Dockerfile, e.g. $PUPPET_SERVER_VERSION

  • (defaults to: '.')

    The directory containing the Dockerfile, defaults to $PWD

  • (defaults to: 'Dockerfile')

    The file name for your dockerfile, defaults to ‘Dockerfile’

  • (defaults to: '')

    A string containing the contents of the Dockerfile [optional]



238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/puppet_docker_tools/utilities.rb', line 238

def get_value_from_variable(variable, directory: '.', dockerfile: 'Dockerfile', dockerfile_contents: '')
  if dockerfile_contents.empty?
    file = "#{directory}/#{dockerfile}"
    fail "File #{file} doesn't exist!" unless File.exist? file
    dockerfile_contents = File.read("#{file}")
  end
  variable_clone = String.new(variable)
  # get rid of the leading $ for the variable
  variable_clone[0] = ''

  dockerfile_contents[/#{variable_clone}=([^\s]+)/, 1]
end

.parse_build_args(build_args) ⇒ Object

parse build args into a hash for easier manipulation

Parameters:

  • array of build_args with each entry in the format ‘arg=value’



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/puppet_docker_tools/utilities.rb', line 32

def parse_build_args(build_args)
  args_hash = {}

  build_args.each do |arg|
    fields = arg.split('=')
    key = fields.first
    # get rid of the key from the fields so we can get the value
    fields.shift
    # join the remaining fields with '=' in case the value had '=' in it
    value = fields.join('=')
    args_hash[key] = value
  end

  args_hash
end

.pull(image) ⇒ Object

Pull a docker image

Parameters:

  • The image to pull. If the image does not include the tag to pull, it will pull all tags for that image



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

def pull(image)
  if image.include?(':')
    puts "Pulling #{image}"
    PuppetDockerTools::Utilities.pull_single_tag(image)
  else
    puts "Pulling all tags for #{image}"
    PuppetDockerTools::Utilities.pull_all_tags(image)
  end
end

.pull_all_tags(image) ⇒ Object

Pull all tags for a docker image

Parameters:

  • The image to pull, e.g. puppet/puppetserver



157
158
159
160
161
162
163
164
165
166
# File 'lib/puppet_docker_tools/utilities.rb', line 157

def pull_all_tags(image)
  Docker::Image.create('fromImage' => image)

  # Filter through existing tags of that image so we can output what we pulled
  images = Docker::Image.all('filter' => image)
  images.each do |img|
    timestamp = PuppetDockerTools::Utilities.format_timestamp(img.info["Created"])
    puts "Pulled #{img.info["RepoTags"].join(', ')}, last updated #{timestamp}"
  end
end

.pull_single_tag(tag) ⇒ Object

Pull a single tag of a docker image

Parameters:

  • The image/tag to pull, e.g. puppet/puppetserver:latest



171
172
173
174
175
# File 'lib/puppet_docker_tools/utilities.rb', line 171

def pull_single_tag(tag)
  image = Docker::Image.create('fromImage' => tag)
  timestamp = PuppetDockerTools::Utilities.format_timestamp(image.info["Created"])
  puts "Pulled #{image.info["RepoTags"].first}, last updated #{timestamp}"
end

.push_to_dockerhub(image_name, stream_output = true) ⇒ Object

Push an image to hub.docker.com

Parameters:

  • The image to push, including the tag e.g., puppet/puppetserver:latest

  • (defaults to: true)

    Whether or not to stream output as it comes in, defaults to true

Returns:

  • Returns an array containing the integer exitstatus of the push command and a string containing the combined stdout and stderr from the push



15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/puppet_docker_tools/utilities.rb', line 15

def push_to_dockerhub(image_name, stream_output=true)
  Open3.popen2e("docker push #{image_name}") do |stdin, output_stream, wait_thread|
    output=''
    while line = output_stream.gets
      if stream_output
        puts line
      end
      output += line
    end
    exit_status = wait_thread.value.exitstatus
    return exit_status, output
  end
end

.update_base_images(tags) ⇒ Object

Pull the specified tags

Parameters:

  • A list of tags to pull, e.g. [‘centos:7’, ‘ubuntu:16.04’]



180
181
182
183
184
# File 'lib/puppet_docker_tools/utilities.rb', line 180

def update_base_images(tags)
  tags.each do |tag|
    PuppetDockerTools::Utilities.pull(tag)
  end
end