Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Docker Private Registry Image Upload

I'm trying to upload images to my private docker registry using the HTTP API, unfortunately without success.

Apparently I haven't understood the upload process yet and would like to ask if anyone can explain this in detail or push me in the right direction.

So far I have tried the following with curl. To experiment I only use the empty alpine image. For that i download it to my workstation with docker pull alpine and then I create tar archive with docker save -o alpine.tar alpine

I then unpack this archive into an alpine directory. This is what the content looks like: ls -R alpine

alpine:
    39cb81dcd06e3d4e2b813f56b72da567696fa9a59b652bd477615b31af969239
    e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a.json
    manifest.json

alpine/39cb81dcd06e3d4e2b813f56b72da567696fa9a59b652bd477615b31af969239:
    json
    layer.tar
    VERSION

According to the documentation I should first initiate the upload by sending a POST to the /v2//blobs/uploads/ URL, for this I execute the following command:

curl -X POST -L -D headers $DOCKER_HOST/v2/alpine/blobs/uploads

The answer is the following:

cat headers

HTTP/1.1 301 Moved Permanently
Docker distribution api version: registry/2.0
location: /v2/alpine/blobs/uploads/
Date: Tu, 21 Jan 2020 12:42:55 GMT
content length: 0

HTTP/1.1 202 Accepted
content length: 0
Docker distribution api version: registry/2.0
Docker upload guide: ed595b3c-1236-46c8-a759-14187fc60e7d
Location: http://<IPADDRESS>/v2/alpine/blobs/uploads/ed595b3c-1236-46c8-a759-14187fc60e7d?_state=GjzU_y-YDQherf4xXO57KyEonSSSwNEM8FiF8rmNfuN7Ik5hbWUiOiJhbHBpbmUiLCJVVUlEIjoiZWQ1OTViM2MtMTIzNi00NmM4LWE3NTktMTQxODdmYzYwZTdkIiwiT2Zmc2V0IjowLCJTdGFydGVkQXQiOiIyMDIwLTAxLTIxVDEyOjQyOjU1LjMxNDYzNTg0NVoifQ%3D%3D
Range: 0-0
X Content Type Options: nosniff
Date: Tu, 21 Jan 2020 12:42:55 GMT

now i would like to do a monolithic upload, as described in the docker-registry documentation.

For this I make the following curl request:

url=http://<IPADDRESS>/v2/alpine/blobs/uploads/ed595b3c-1236-46c8-a759-14187fc60e7d?_state=GjzU_y-YDQherf4xXO57KyEonSSSwNEM8FiF8rmNfuN7Ik5hbWUiOiJhbHBpbmUiLCJVVUlEIjoiZWQ1OTViM2MtMTIzNi00NmM4LWE3NTktMTQxODdmYzYwZTdkIiwiT2Zmc2V0IjowLCJTdGFydGVkQXQiOiIyMDIwLTAxLTIxVDEyOjQyOjU1LjMxNDYzNTg0NVoifQ%3D%3D?digest=sha256:39cb81dcd06e3d4e2b813f56b72da567696fa9a59b652bd477615b31af969239 

layerpath=/home/user/alpine/39cb81dcd06e3d4e2b813f56b72da567696fa9a59b652bd477615b31af969239/layer.tar

curl -X PUT -H "Content-Type=application/octet-stream" --data-binary @"$layerpath"  $url

For this I receive an answer that means nothing to me:

 {"errors":[{"code": "BLOB_UPLOAD_INVALID", "message": "blob upload invalid", "detail":212}]}

I don't know what I'm doing wrong. For any help I am very grateful.

like image 798
H4x9r Avatar asked May 01 '26 16:05

H4x9r


1 Answers

Okay, I figured it out. For all the others who are also dealing with this question here is a little script which should clarify the handling of the HTTP-Api:

#!/bin/bash


# Global Variables

MANIFEST="./Manifest.json"

DOCKER_HOST=$1
REPOSITORY=$2
LAYERPATH=$3
CONFIGPATH=$4

SIZE=
DIGEST=

LOCATION=


CONFIGSIZE=
CONFIGDIGEST=
LAYERSIZE=
LAYERDIGEST=

# Functions

function initiateUpload(){
   LOCATION=$(curl -X POST -siL -v  -H "Connection: close" $DOCKER_HOST/v2/$REPOSITORY/blobs/uploads | grep Location | sed '2q;d' | cut -d: -f2- | tr -d ' ' | tr -d '\r')
}


function patchLayer(){
    layersize=$(stat -c%s "$1")
    LOCATION=$(curl -X PATCH -v -H "Content-Type: application/octet-stream" \
        -H "Content-Length: $layersize" -H "Connection: close" --data-binary @"$1" \
        $LOCATION 2>&1 | grep 'Location' | cut -d: -f2- | tr -d ' ' | tr -d '\r')
    SIZE=$layersize
}

function putLayer(){
    DIGEST=sha256:$(sha256sum $1 | cut -d ' ' -f1)
    url="$LOCATION&digest=$DIGEST"
    curl -X PUT -v -H "Content-Length: 0" -H "Connection: close" $url

}

function uploadManifest(){
    ((size=$(stat -c%s "$MANIFEST")-1))
    curl -X PUT -vvv -H "Content-Type:   application/vnd.docker.distribution.manifest.v2+json"\
    -H "Content-Length: $size" -H "Connection: close" \
    -d "$(cat "$MANIFEST")" $DOCKER_HOST/v2/$REPOSITORY/manifests/latest
}

# Check Parameters

if [ $# -lt 4 ] 
    then
        echo "Error: No arguments supplied."
        echo "Usage: upload.sh <DOCKER_HOST> <REPOSITORY> <LAYER> <CONFIG>"
       exit 1
fi

#upload Layer
initiateUpload 
patchLayer $LAYERPATH
LAYERSIZE=$SIZE
putLayer $LAYERPATH
LAYERDIGEST=$DIGEST

#upload Config

initiateUpload
patchLayer $CONFIGPATH
CONFIGSIZE=$SIZE
putLayer $CONFIGPATH
CONFIGDIGEST=$DIGEST


cat > $MANIFEST << EOF
{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
   "config": {
       "mediaType": "application/vnd.docker.container.image.v1+json",
       "size": $CONFIGSIZE,
       "digest": "$CONFIGDIGEST"
   },
   "layers": [
        {
            "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
            "size":$LAYERSIZE,
            "digest": "$LAYERDIGEST"
        }
    ]
 }
 EOF

 uploadManifest

It's not pretty, but it works for that.

If you need more information, I will gladly extend this answer

like image 50
H4x9r Avatar answered May 04 '26 12:05

H4x9r



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!