inspec/lib/resources/docker_image.rb
Adam Leff 3ffaee91c2 docker_image resource: properly handle registries in image strings (#2356)
When supplying a docker image that contains a registry with a port number,
such as `localhost:5000/chef/inspec:1.46.3`, the docker_image resource
was unable to locate the image in question due to incorrect parsing
of the repository and tag.

Signed-off-by: Adam Leff <adam@leff.co>
2017-12-01 10:24:15 +01:00

124 lines
3.7 KiB
Ruby

# encoding: utf-8
#
# Copyright 2017, Christoph Hartmann
#
# author: Christoph Hartmann
# author: Patrick Muench
# author: Dominik Richter
module Inspec::Resources
class DockerImage < Inspec.resource(1)
name 'docker_image'
desc ''
example "
describe docker_image('alpine:latest') do
it { should exist }
its('id') { should_not eq '' }
its('image') { should eq 'alpine:latest' }
its('repo') { should eq 'alpine' }
its('tag') { should eq 'latest' }
end
describe docker_image('alpine:latest') do
it { should exist }
end
describe docker_image(id: '4a415e366388') do
it { should exist }
end
"
def initialize(opts = {})
# do sanitizion of input values
o = opts.dup
o = { image: opts } if opts.is_a?(String)
@opts = sanitize_options(o)
end
def exist?
image_info.exists?
end
def id
image_info.ids[0] if image_info.entries.size == 1
end
def image
"#{repo}:#{tag}" if image_info.entries.size == 1
end
def repo
image_info.repositories[0] if image_info.entries.size == 1
end
def tag
image_info.tags[0] if image_info.entries.size == 1
end
def to_s
img = @opts[:image] || @opts[:id]
"Docker Image #{img}"
end
private
def sanitize_options(opts)
opts.merge!(parse_components_from_image(opts[:image]))
# assume a "latest" tag if we don't have one
opts[:tag] ||= 'latest'
# if the ID isn't nil and doesn't contain a hash indicator (indicated by the presence
# of a colon, which separates the indicator from the actual hash), we assume it's sha256.
opts[:id] = 'sha256:' + opts[:id] unless opts[:id].nil? || opts[:id].include?(':')
# Assemble/reassemble the image from the repo and tag
opts[:image] = "#{opts[:repo]}:#{opts[:tag]}" unless opts[:repo].nil?
# return the santized opts back to the caller
opts
end
def image_info
return @info if defined?(@info)
opts = @opts
@info = inspec.docker.images.where {
(repository == opts[:repo] && tag == opts[:tag]) || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id])))
}
end
def parse_components_from_image(image_string)
# if the user did not supply an image string, they likely supplied individual
# option parameters, such as repo and tag. Return empty data back to the caller.
return {} if image_string.nil?
first_colon = image_string.index(':') || -1
first_slash = image_string.index('/') || -1
if image_string.count(':') == 2
# If there are two colons in the image string, it contains a repo-with-port and a tag.
# example: localhost:5000/chef/inspec:1.46.3
partitioned_string = image_string.rpartition(':')
repo = partitioned_string.first
tag = partitioned_string.last
elsif image_string.count(':') == 1 && first_colon < first_slash
# If there's one colon in the image string, and it comes before a forward-slash,
# it contains a repo-with-port but no tag.
# example: localhost:5000/ubuntu
repo = image_string
tag = nil
else
# If there's one colon in the image string and it doesn't preceed a slash, or if
# there is no colon at all, then it separates the repo from the tag, if there is a tag.
# example: chef/inspec:1.46.3
# example: chef/inspec
# example: ubuntu:14.04
repo, tag = image_string.split(':')
end
# return the repo and tag parsed from the string, which can be merged into
# the rest of the user-supplied options
{ repo: repo, tag: tag }
end
end
end