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>
This commit is contained in:
Adam Leff 2017-12-01 04:24:15 -05:00 committed by Christoph Hartmann
parent 222d4b6aac
commit 3ffaee91c2
2 changed files with 94 additions and 17 deletions

View file

@ -63,25 +63,19 @@ module Inspec::Resources
private private
def sanitize_options(opts) def sanitize_options(opts)
if !opts[:image].nil? opts.merge!(parse_components_from_image(opts[:image]))
if !opts[:image].index(':').nil?
repo, tag = opts[:image].split(':')
else
opts[:repo] = opts[:image]
opts[:image] = nil
end
opts[:repo] ||= repo
opts[:tag] ||= tag
end
if !opts[:id].nil?
if opts[:id].index(':').nil?
opts[:id] = 'sha256:' + opts[:id]
end
end
# assume a "latest" tag if we don't have one
opts[:tag] ||= 'latest' opts[:tag] ||= 'latest'
opts[:image] ||= "#{opts[:repo]}:#{opts[:tag]}" unless opts[:repo].nil?
# 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 opts
end end
@ -92,5 +86,39 @@ module Inspec::Resources
(repository == opts[:repo] && tag == opts[:tag]) || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id]))) (repository == opts[:repo] && tag == opts[:tag]) || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id])))
} }
end 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
end end

View file

@ -19,4 +19,53 @@ describe 'Inspec::Resources::DockerImage' do
resource.to_s.must_equal 'Docker Image alpine:latest' resource.to_s.must_equal 'Docker Image alpine:latest'
end end
end end
describe '#parse_components_from_image' do
let(:resource) { load_resource('docker_image', 'alpine') }
let(:parsed) { resource.send(:parse_components_from_image, image_string) }
describe 'a nil image string' do
let(:image_string) { nil }
it 'returns an empty hash' do
parsed.must_equal({})
end
end
describe 'an image string containing a simple repo' do
let(:image_string) { 'chef/inspec' }
it 'returns correct data' do
parsed[:repo].must_equal 'chef/inspec'
parsed[:tag].must_be_nil
end
end
describe 'parses an image string containing a repo with a port number' do
let(:image_string) { 'localhost:5000/chef/inspec' }
it 'returns correct data' do
parsed[:repo].must_equal 'localhost:5000/chef/inspec'
parsed[:tag].must_be_nil
end
end
describe 'parses an image string containing a repo with a tag' do
let(:image_string) { 'chef/inspec:1.46.3' }
it 'returns correct data' do
parsed[:repo].must_equal 'chef/inspec'
parsed[:tag].must_equal '1.46.3'
end
end
describe 'parses an image string containing a repo with a port number and a tag' do
let(:image_string) { 'localhost:5000/chef/inspec:1.46.3' }
it 'returns correct data' do
parsed[:repo].must_equal 'localhost:5000/chef/inspec'
parsed[:tag].must_equal '1.46.3'
end
end
end
end end