Key Errors
If you see this error:
sregistry pull docker://gcr.io/deepvariant-docker/deepvariant:latest
...
KeyError: 'schemaVersion'
1. Why might this happen?
It’s likely that the manifest being returned by the Docker API isn’t what is
expected. It could either be a permissions issue, or a problem with the name
(unique resource identifier) you have provided, and the manifest isn’t
found (akin to a 404). The reason we get a “KeyError” is because the json
response from the API is different than expected, and the key doesn’t exist.
For the example below I’m using sregistry version 0.0.86
2. Interactive Python Shell
What we can do is to get the manifest interactively, and see if we can catch that error.
First, open an interactive sregistry shell, and prefix with the SREGISTRY_CLIENT=docker
to specify that you want a docker client.
# SREGISTRY_CLIENT=docker sregistry shell
# The client is loaded - make sure it's docker
client
[client][docker]
3. Define Image
Next, let’s define our image. This is the full unique resource identifier (with gcr.io)
image = "docker://gcr.io/deepvariant-docker/deepvariant:latest"
Load some utility functions, and the base as gcr.io
# Load utils at the start, we need a few
from sregistry.utils import *
# This should be gcr.io
base = client._update_base(image)
base
'gcr.io'
To make life easier, sregistry has a standard function for parsing the unique resource identifier into pieces we commonly need like tag, version, etc.
q = parse_image_name(remove_uri(image), base=base)
q
# {'collection': 'deepvariant-docker',
# 'image': 'deepvariant',
# 'storage': 'deepvariant-docker/deepvariant:latest.simg',
# 'tag': 'latest',
# 'uri': 'deepvariant-docker/deepvariant:latest',
# 'url': 'deepvariant-docker/deepvariant',
# 'version': None}
4. Get Manifests
Now let’s walk through getting the manifests, which is the step we normally
do to get layers. If you get a key error of schemaVersion
, this is where you are hitting issues. For curious users, these steps are akin to walking through the function client._download_layers
. The repository name coincides with the url in the dictionary
repo_name = q['url']
'deepvariant-docker/deepvariant'
The digest is going to be a version (e.g., if there is @ in the uri) or if the version
if None
, we use the tag.
digest = q['version'] or q['tag']
`latest`
Now let’s get the manifests. This is likely where the error is going to trigger. Here is a quick loop that will let us look at what is returned from the API
results = []
schemaVersions = ['v1', 'v2', 'config']
for schemaVersion in schemaVersions:
manifest = client._get_manifest(repo_name, digest, schemaVersion)
results.append(manifest)
The result that I get here is that the “v2” was found - meaning we have a list
with [None, <manifest>, None]
. Here is what I see for the manifest:
{'config': {'digest': 'sha256:8178530253d0a434677b7f6e20d45a9c24179fc88a8c24ef37ab89c1ef6754cf',
'mediaType': 'application/vnd.docker.container.image.v1+json',
'size': 7602},
'layers': [{'digest': 'sha256:297061f60c367c17cfd016c97a8cb24f5308db2c913def0f85d7a6848c0a17fa',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 43026850},
{'digest': 'sha256:e9ccef17b516e916aa8abe7817876211000c27150b908bdffcdeeba938cd004c',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 850},
{'digest': 'sha256:dbc33716854d9e2ef2de9769422f498f5320ffa41cb79336e7a88fbb6c3ef844',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 621},
{'digest': 'sha256:8fe36b178d25214195af42254bc7d5d64a269f654ef8801bbeb0b6a70a618353',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 851},
{'digest': 'sha256:686596545a94a0f0bf822e442cfd28fbd8a769f28e5f4018d7c24576dc6c3aac',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 169},
{'digest': 'sha256:85fa0ac43c65aa8973c9b4382fa6a8074519ac9de874dba2bc35b2631742edf3',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 1302},
{'digest': 'sha256:45f2f6ccc5ee24e9a3abb2cce3fecb46ad43c4ea49e35446c148b34a0292e1f6',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 173},
{'digest': 'sha256:3ed22810c25d3ee9fcbd02a3428f04ca49783db74515bd8499c3534321bf8d99',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 3843},
{'digest': 'sha256:0080cefaf4ae6dd2fa6becd3223412d0802d06021939a19a9a5760308f7681d6',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 431972422},
{'digest': 'sha256:35ed20f5cf41686f7bd63f00f84b2338b1b5cce00e03a2927ae4b252ca123ef4',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 29495475},
{'digest': 'sha256:a6fb1b69768eec580fd031f4626afe775ea6f71ac3ab5b372a3a9f9040651d06',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 332}],
'mediaType': 'application/vnd.docker.distribution.manifest.v2+json',
'schemaVersion': 2,
'selfLink': 'https://gcr.io/v2/deepvariant-docker/deepvariant/manifests/latest'}
At this point, we would use the layers
key to get a list of layers, and dump
them into the container we are building. But if you previously got a KeyError then
you didn’t get this far. You probably got a response of all None
([None,None,None]
).
We need to dig deeper!
5. Response of all None
Oh no, you got None for all the queries! Let’s modify the loop slightly to inspect one level deeper. First, define a header lookup table. This is actually what drives which version we “ask for”:
accepts = {'config': "application/vnd.docker.container.image.v1+json",
'v1': "application/vnd.docker.distribution.manifest.v1+json",
'v2': "application/vnd.docker.distribution.manifest.v2+json" }
We are still going to check each of the schema versions, because different registries (unfortunately) serve different versions. We are also going to use the base requests library instead of the sregistry client get function (that parses responses / errors and would handle them for us)
# use requests library
import requests
results = []
# Iterate through schema versions
schemaVersions = ['v1', 'v2', 'config']
for schemaVersion in schemaVersions:
# The url to send the request to
url = client._get_manifest_selfLink(repo_name, digest)
# https://gcr.io/v2/deepvariant-docker/deepvariant/manifests/latest
# Prepare headers from "accept" above
headers = client.headers.copy()
headers['Accept'] = accepts[schemaVersion]
# Here is where an error might trigger, or you get an unexpected response
manifest = requests.get(url, headers=headers)
# Append the result for inspection
results.append(manifest)
6. Inspect API Responses
Results is a list of response objects. We can inspect each to understand what is going on.
Our first result was looking for a schemaVersion
of v1
which is likely not implemented
for gcr.io
. We can see from the result that we got a 404, “not found.”
results[0]
<Response [404]>
We can re-verify this by looking at the json from this response, it indicates more detail about the error:
results[0].json()
{'errors': [{'code': 'MANIFEST_UNKNOWN',
'message': "Manifest with tag 'latest' has media type 'application/vnd.docker.distribution.manifest.v2+json', but client accepts 'application/vnd.docker.distribution.manifest.v1+json'."}]}
This is saying “hey, you are only accepting a version 1 manifest, but this manifest is verison 2. This is a really good example of why it’s important to
read the error output in full!
It’s telling you what’s wrong! So logically, what shoul we do? We should ask for the
version 2 manifest, of course! The v2
schemaVersion request is the next entry
in the list of results:
results[1]
<Response [200]>
is a success! 200 indicates all is OK. It follows then, that the json()
output is
the manifest that we were asking for. If you were getting an error, you probably have a different response code here, and likely a different error message. Look at this
carefully to determine why you aren’t getting the manifest. If all is well, you
should see this:
results[1].json()
{'config': {'digest': 'sha256:8178530253d0a434677b7f6e20d45a9c24179fc88a8c24ef37ab89c1ef6754cf',
'mediaType': 'application/vnd.docker.container.image.v1+json',
'size': 7602},
'layers': [{'digest': 'sha256:297061f60c367c17cfd016c97a8cb24f5308db2c913def0f85d7a6848c0a17fa',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 43026850},
{'digest': 'sha256:e9ccef17b516e916aa8abe7817876211000c27150b908bdffcdeeba938cd004c',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 850},
{'digest': 'sha256:dbc33716854d9e2ef2de9769422f498f5320ffa41cb79336e7a88fbb6c3ef844',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 621},
{'digest': 'sha256:8fe36b178d25214195af42254bc7d5d64a269f654ef8801bbeb0b6a70a618353',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 851},
{'digest': 'sha256:686596545a94a0f0bf822e442cfd28fbd8a769f28e5f4018d7c24576dc6c3aac',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 169},
{'digest': 'sha256:85fa0ac43c65aa8973c9b4382fa6a8074519ac9de874dba2bc35b2631742edf3',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 1302},
{'digest': 'sha256:45f2f6ccc5ee24e9a3abb2cce3fecb46ad43c4ea49e35446c148b34a0292e1f6',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 173},
{'digest': 'sha256:3ed22810c25d3ee9fcbd02a3428f04ca49783db74515bd8499c3534321bf8d99',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 3843},
{'digest': 'sha256:0080cefaf4ae6dd2fa6becd3223412d0802d06021939a19a9a5760308f7681d6',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 431972422},
{'digest': 'sha256:35ed20f5cf41686f7bd63f00f84b2338b1b5cce00e03a2927ae4b252ca123ef4',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 29495475},
{'digest': 'sha256:a6fb1b69768eec580fd031f4626afe775ea6f71ac3ab5b372a3a9f9040651d06',
'mediaType': 'application/vnd.docker.image.rootfs.diff.tar.gzip',
'size': 332}],
'mediaType': 'application/vnd.docker.distribution.manifest.v2+json',
'schemaVersion': 2}
The last result in the list is the same as the first, it’s a 404 because the manifest accept header doesn’t match what the registry is offering.
7. Another Error?
At this point, either you will get an obvious message returned from the API (something akin to telling you that you don’t have correct permissions for an operation) or in the case that you don’t understand the error and need help, please open an issue!