docker_image_exists() {
local image_full_name="$1"; shift
local wait_time="${1:-5}"
local search_term='Pulling|is up to date|not found'
local result="$((timeout --preserve-status "$wait_time" docker 2>&1 pull "$image_full_name" &) | grep -v 'Pulling repository' | egrep -o "$search_term")"
test "$result" || { echo "Timed out too soon. Try using a wait_time greater than $wait_time..."; return 1 ;}
echo $result | grep -vq 'not found'
}
Usage example:
docker_image_exists elifarley/docker-dev-env:alpine-sshd && \
echo EXISTS || \
echo "Image does not exist"
function docker_tag_exists() {
curl --silent -f -lSL https://index.docker.io/v1/repositories/$1/tags/$2 > /dev/null
}
if docker_tag_exists library/nginx 1.7.5; then
echo exist
else
echo not exists
fi
Update:
In case of usage Docker Registry v2 (based on this):
# set username and password
UNAME="user"
UPASS="password"
function docker_tag_exists() {
TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${UNAME}'", "password": "'${UPASS}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token)
curl --silent -f --head -lL https://hub.docker.com/v2/repositories/$1/tags/$2/ > /dev/null
}
if docker_tag_exists library/nginx 1.7.5; then
echo exist
else
echo not exists
fi
I was struggling getting this to work for a private docker hub repository and finally decided to write a ruby script instead, which works as of today. Feel free to use!
#!/usr/bin/env ruby
require 'base64'
require 'net/http'
require 'uri'
def docker_tag_exists? repo, tag
auth_string = Base64.strict_encode64 "#{ENV['DOCKER_USER']}:#{ENV['DOCKER_PASSWORD']}"
uri = URI.parse("https://registry.hub.docker.com/v1/repositories/#{repo}/tags/#{tag}")
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Basic #{auth_string}"
request['Accept'] = 'application/json'
request['Content-Type'] = 'application/json'
response = Net::HTTP.start(request.uri.hostname, request.uri.port, use_ssl: true) do |http|
http.request(request)
end
(response.body == 'Tag not found') ? 0 : 1
end
exit docker_tag_exists? ARGV[0], ARGV[1]
Note: you need to specify DOCKER_USER and DOCKER_PASSWORD when calling this like...
This line would print out 'latest', if authentication is successful and the specified tag does not exists! I was using this in my Vagrantfile when trying to fetch a tag based on the current git branch:
git rev-parse --symbolic-full-name --abbrev-ref HEAD
DOCKER_CLI_EXPERIMENTAL Enable experimental features for the cli (e.g. enabled or disabled)
The snippet therefore becomes:
tag=something
if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect $image:$tag >/dev/null; then
Do nothing
else
Build and push docker image with that tag
fi
Just a small improvement of Evgeny Oskin's solution. When it comes to a user repo that hasn't been created yet, jq says that it "Cannot iterate over null". To overcome it. one can skip not present blocks with ?
Here is a modification to above mentioned solution that is applicable to a public repo in particular:
#!/usr/bin/env bash
function docker_image_tag_exists() {
EXISTS=$(curl -s https://hub.docker.com/v2/repositories/$1/tags/?page_size=10000 | jq -r "[.results? | .[]? | .name == \"$2\"] | any")
test ${EXISTS} = true
}
if docker_image_tag_exists $1 $2; then
echo "true"
else
echo "false"
fi
I have a docker private repo stood up on my LAN using registry:2, private CA, and basic auth.
I just looked at the official docker API docs (https://docs.docker.com/registry/spec/api/) and came up with this solution which seems pretty elegant, easy to debug, customize, and is CICD/scripting friendly.
All of the options above assume that you can authenticate using username/password. There are a lot of cases where this is inconvenient, for example when using Google Container Registry, for which one would run gcloud auth configure-docker gcr.io first. That command installs an authentication helper for Docker, and you wouldn't want to manage that token yourself.
One tool that supports these docker authentication helpers, and also allows getting a manifest - like experimental Docker - is crane.
Example using crane:
# you would have done this already
gcloud auth configure-docker gcr.io;
# ensure we have crane installed
which crane || (echo 'installing crane' && GO111MODULE=on go get -u github.com/google/go-containerregistry/cmd/crane)
# check manifest
crane manifest ubuntu || echo "does not exist"
If you are querying Hub for the existence of a tag, make sure you use a HEAD rather than a GET request. The GET request count against your rate limit. A script that does this specific to Docker Hub, only supports the Docker media types, and anonymous logins that are rate limited by your requesting IP, looks like:
$ more ~/data/docker/registry-api/manifest-v2-head.sh
#!/bin/sh
ref="${1:-library/ubuntu:latest}"
sha="${ref#*@}"
if [ "$sha" = "$ref" ]; then
sha=""
fi
wosha="${ref%%@*}"
repo="${wosha%:*}"
tag="${wosha##*:}"
if [ "$tag" = "$wosha" ]; then
tag="latest"
fi
api="application/vnd.docker.distribution.manifest.v2+json"
apil="application/vnd.docker.distribution.manifest.list.v2+json"
token=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repo}:pull"
\
| jq -r '.token')
curl -H "Accept: ${api}" -H "Accept: ${apil}" \
-H "Authorization: Bearer $token" \
-I -s "https://registry-1.docker.io/v2/${repo}/manifests/${sha:-$tag}"
To work on other registries, handle more media types (like the OCI types), and handle logins, use a tool like crane, skopeo, or my own regclient:
# the "image digest" command uses a HEAD instead of a GET
if regctl image digest registry.example.com/repo:tag >/dev/null 2>&1; then
echo tag exists
else
echo tag does not exist
fi