Check if image:tag combination already exists on d

2020-01-29 05:20发布

问题:

As part of a bash script, I want to check if a particularly docker image:tag combination exists on docker hub. Also, it will be a private repository.

i.e. the pseudocode would be like:

tag = something
if image:tag already exists on docker hub:
    Do nothing
else
    Build and push docker image with that tag

回答1:

Please try this one

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)
    EXISTS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/$1/tags/?page_size=10000 | jq -r "[.results | .[] | .name == \"$2\"] | any")
    test $EXISTS = true
}

if docker_tag_exists library/nginx 1.7.5; then
    echo exist
else 
    echo not exists
fi


回答2:

Update: Docker-Free solution see below

Using Docker

This is the solution I use with gitlab using the docker:stable image.

Make sure experimental client-features are enabled:

Set the environment variable DOCKER_CLI_EXPERIMENTAL to enabled (See Matěj's answer below)

Alternatively adjust the config (original answer):

echo '{"experimental": "enabled"}' > ~/.docker/config.json

This will also overwrite your config. If that is not an option you need to do that manually or use jq, sed or whatever you have available.

Login

docker login -u $USER -p $PASSWORD $REGISTRY

Check whether it's there:

docker manifest inspect $IMGNAME:$IMGTAG > /dev/null ; echo $?

docker will return 0 on success or 1 on failure.

Update: If you don't have access to a docker-daemon, e.g. because you are building a docker image using kaniko within a docker, you can use the registry-api scripts provided by harbor. Note that they are python2.



回答3:

To build on morty's answer, notice that docker supports setting the experimental flag using environment variable:

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


回答4:

Easiest:

docker pull alpine:invalid > /dev/null && echo "success" || echo "failed"

Pulls & prints success if image exists, or prints failed if it doesn't:

You can even export it in a var if using in bash script:



回答5:

Here's a Bash function that will help:

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"


回答6:

I like solutions based on docker.

This oneliner is what I use in our CI:

 docker run --rm anoxis/registry-cli -l user:password -r registry-url -i docker-name | grep -q docker-tag || echo do something if not found


回答7:

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


回答8:

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...

DOCKER_USER=XXX DOCKER_PASSWORD=XXX config/docker/docker_hub.rb "NAMESPACE/REPO" "TAG" && echo 'latest'

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



回答9:

You could just try pulling and see if you're up to date (You may want to tag your local image first though in case the remote is newer):

docker pull deepdriveio/deepdrive:env-3.0
env-3.0: Pulling from deepdriveio/deepdrive
Digest: sha256:3b6b6514f79a551b47896f908a2de00b55df1db22f5908c8436feaa12310dfa3
Status: Image is up to date for deepdriveio/deepdrive:env-3.0


回答10:

Have you tried something like that, simply trying to pull the tag and deciding to push or not according to the return code?

#! /bin/bash

if docker pull hello-world:linux > /dev/null; then
  echo "already exist -> do not push"
else
  echo "does not exist -> push"
fi