Docker Registry HTTP API V2で様々なコンテナレジストリからタグの一覧を取得する

# Docker Registry HTTP API V2とは？

コンテナイメージリポジトリには様々な種類があります。最もよく聞くのは老舗のDocker Hub、他にはQuay.ioやGitHub Container Registry(ghcr)などもあります。

これらはそれぞれ別の実装ですが、これらで共通して利用できるが、Docker Registry HTTP API V2です。

仕様についてはDocer社のページにまとまっています

https://docs.docker.com/registry/spec/api/

# タグの一覧を取得する

上記文書によるとタグの取得は`GET /v2/<name>/tags/list`でできそうです。

このAPIはページネーションをサポートしており`n`パラメータと、`last`パラメータを使うことで複数ページの結果を取得できます。

というのが、文書にある内容なのですが、意外とこれが、各コンテナレジストリで違うふるまいをしたので、メモ的に状況を紹介します。

## skopeo

各コンテナレジストリのAPIの振る舞いを観察するために、skopeo実行時のログを活用することにしました。このツールはPodmanの開発コミュニティを中心にメンテされているもののようです。

https://github.com/containers/skopeo

skopeoはコンテナイメージレジストリの操作を行うためのCLIツールで、コンテナレジストリの違いを意識せずに、操作するためのものです。

引数に`--debug`をつけると、詳細なログを出しつつ動いてくれるので、この動作を追うことで各コンテナレジストリの実装について動作を確かめてみました。

ログからわからない動作については、以下のライブラリのソースコードを読むことも解析の助けになりました。

https://github.com/containers/image

# DockerHub

まずはskopeoでの実行をしてみます

```
$ docker run --rm quay.io/skopeo/stable:v1.9.2 list-tags --debug docker://ubuntu
time="2023-03-28T01:47:19Z" level=debug msg="Using registries.d directory /etc/containers/registries.d"
time="2023-03-28T01:47:19Z" level=debug msg="Loading registries configuration \"/etc/containers/registries.conf\""
time="2023-03-28T01:47:19Z" level=debug msg="Loading registries configuration \"/etc/containers/registries.conf.d/000-shortnames.conf\""
time="2023-03-28T01:47:19Z" level=debug msg="No credentials matching docker.io/library/ubuntu found in /tmp/auth.json"
time="2023-03-28T01:47:19Z" level=debug msg="No credentials matching docker.io/library/ubuntu found in /root/.config/containers/auth.json"
time="2023-03-28T01:47:19Z" level=debug msg="No credentials matching docker.io/library/ubuntu found in /root/.docker/config.json"
time="2023-03-28T01:47:19Z" level=debug msg="No credentials matching docker.io/library/ubuntu found in /root/.dockercfg"
time="2023-03-28T01:47:19Z" level=debug msg="No credentials for docker.io/library/ubuntu found"
time="2023-03-28T01:47:19Z" level=debug msg=" Lookaside configuration: using \"default-docker\" configuration"
time="2023-03-28T01:47:19Z" level=debug msg=" No signature storage configuration found for docker.io/library/ubuntu:latest, using built-in default file:///var/lib/containers/sigstore"
time="2023-03-28T01:47:19Z" level=debug msg="Looking for TLS certificates and private keys in /etc/docker/certs.d/docker.io"
time="2023-03-28T01:47:19Z" level=debug msg=" Sigstore attachments: using \"default-docker\" configuration"
time="2023-03-28T01:47:19Z" level=debug msg="GET https://registry-1.docker.io/v2/"
time="2023-03-28T01:47:19Z" level=debug msg="Ping https://registry-1.docker.io/v2/ status 401"
time="2023-03-28T01:47:19Z" level=debug msg="GET https://auth.docker.io/token?scope=repository%3Alibrary%2Fubuntu%3Apull&service=registry.docker.io"
time="2023-03-28T01:47:20Z" level=debug msg="GET https://registry-1.docker.io/v2/library/ubuntu/tags/list"
{
    "Repository": "docker.io/library/ubuntu",
    "Tags": [
        "10.04",
        "12.04",
        "12.04.5",
...jsonでタグが列挙されている...
```

DockerHubのリポジトリのイメージはドメイン名なしで`ubuntu:latest`のように指定できます。
このURLは　`docker.io/library/ubuntu`と解釈され、そのイメージは`https://registry-1.docker.io/v2/library/ubuntu`にあると解釈されるようです。
（この部分のロジックは実装にベタ書きされているように見えました）

まず以下にアクセスすると401が返却されます。

```
$ curl -v "https://registry-1.docker.io/v2/"
...
< HTTP/1.1 401 Unauthorized
< content-type: application/json
< docker-distribution-api-version: registry/2.0
< www-authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io"
< date: Tue, 28 Mar 2023 01:40:10 GMT
< content-length: 87
< strict-transport-security: max-age=31536000
<
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":null}]}
```

公開リポジトリであってもこのエラーが返るようなので認証が必要そうです。
といっても、ログインをする必要はなさそうです。

認証のための情報は　`www-authenticate`ヘッダに記載されています。

この情報を頼りにアクセスのためのトークンを取得します。

`https://auth.docker.io/token?scope=repository%3Alibrary%2Fubuntu%3Apull&service=registry.docker.io`

このURLをよく見てみると、以下のようにアクセスしたいリソースを指定しているようです。
- scope=repository/library/ubuntu:pull
     - これはこれからアクセスしようとするリポジトリから類推している？
- service=registry.docker.io
     - これは上記www-authenticateヘッダから取得したもの

この仕様を探すと・・以下の文書が見つかりました

`Docker Registry v2 authentication via central service`ということで、これもDocker Registry HTTP API V2にアクセスするための仕様の一部のようです。

https://docs.docker.com/registry/spec/auth/token/

ということでアクセスしてみます。出力はjsonなのでjqで見やすく加工します。

```
$ curl "https://auth.docker.io/token?scope=repository%3Alibrary%2Fubuntu%3Apull&service=registry.docker.io"| jq -r
{
  "token": ....
  "access_token: ...
  "expires_in: 300,
  "issued_at": ...
}
```

それっぽいトークンを得ることが出来ました。必要に応じてユーザ認証情報も渡す必要があるようでしたが、publicなイメージの場合は不要のようでした。

`token`と`access_token`は同じトークンがセットされていました。

トークンが得られたので、それを使ってDocker Registry HTTP API V2のAPIを利用してみます。
ちなみにこのトークンは300sで有効期限が切れてしまうので注意してください。(上記の`expires_in`)

```
$ curl -H "Authorization: Bearer <上記のtoken>" "https://registry-1.docker.io/v2/library/ubuntu/tags/list"
{"name":"library/ubuntu","tags":["10.04","12.04","12.04.5","12.10","13.04",...
```

無事にタグの一覧を取得出来ました。

ちなみにここで`n`を指定すると取得する個数を制限でき、無指定だとすべてのタグが取得できました。

```
$ curl -v -H "Authorization: Bearer <上記のaccess_token>" "https://registry-1.docker.io/v2/library/ubuntu/tags/list?n=2"
...
< HTTP/1.1 200 OK
< content-type: application/json
< docker-distribution-api-version: registry/2.0
< link: </v2/library/ubuntu/tags/list?last=12.04&n=2>; rel="next"
< date: Tue, 28 Mar 2023 05:24:22 GMT
< content-length: 51
< strict-transport-security: max-age=31536000
<
{ [51 bytes data]
100    51  100    51    0     0     64      0 --:--:-- --:--:-- --:--:--    64
* Connection #0 to host registry-1.docker.io left intact
* Closing connection 0
{
  "name": "library/ubuntu",
  "tags": [
    "10.04",
    "12.04"
  ]
}
```

さらに`n`と一緒に`last`に前回リクエストの最後のtagの値を指定することで、次のページを取得することが出来ました。このアクセス方法は上記レスポンスの`link`ヘッダに記載されているものです。

```
$ curl -v -H "Authorization: Bearer <上記のaccess_token>" "https://registry-1.docker.io/v2/library/ubuntu/tags/list?last=12.04&n=2"
...
< HTTP/1.1 200 OK
< content-type: application/json
< docker-distribution-api-version: registry/2.0
< link: </v2/library/ubuntu/tags/list?last=12.10&n=2>; rel="next"
< date: Tue, 28 Mar 2023 05:25:52 GMT
< content-length: 53
< strict-transport-security: max-age=31536000
<
{ [53 bytes data]
100    53  100    53    0     0     62      0 --:--:-- --:--:-- --:--:--    62
* Connection #0 to host registry-1.docker.io left intact
* Closing connection 0
{
  "name": "library/ubuntu",
  "tags": [
    "12.04.5",
    "12.10"
  ]
}
```

## Docker Hubの独自API

Docker Registry HTTP API V2とは別にDocker HubのAPIというものもあります

https://docs.docker.com/docker-hub/api/latest/

`https://registry.hub.docker.com/v2/repositories` がエントリポイントのようです。
Docker Registry HTTP API V2とはドメインが違います。

# GitHub Container Registry (ghcr.io)

DockerHubと同じか？、、と思うとちょっと振る舞いが違いました。

```
$ docker run --rm quay.io/skopeo/stable:v1.9.2 list-tags --debug docker://ghcr.io/linuxserver/hedgedoc
time="2023-03-28T05:28:58Z" level=debug msg="Using registries.d directory /etc/containers/registries.d"
time="2023-03-28T05:28:58Z" level=debug msg="Loading registries configuration \"/etc/containers/registries.conf\""
time="2023-03-28T05:28:58Z" level=debug msg="Loading registries configuration \"/etc/containers/registries.conf.d/000-shortnames.conf\""
time="2023-03-28T05:28:58Z" level=debug msg="No credentials matching ghcr.io/linuxserver/hedgedoc found in /tmp/auth.json"
time="2023-03-28T05:28:58Z" level=debug msg="No credentials matching ghcr.io/linuxserver/hedgedoc found in /root/.config/containers/auth.json"
time="2023-03-28T05:28:58Z" level=debug msg="No credentials matching ghcr.io/linuxserver/hedgedoc found in /root/.docker/config.json"
time="2023-03-28T05:28:58Z" level=debug msg="No credentials matching ghcr.io/linuxserver/hedgedoc found in /root/.dockercfg"
time="2023-03-28T05:28:58Z" level=debug msg="No credentials for ghcr.io/linuxserver/hedgedoc found"
time="2023-03-28T05:28:58Z" level=debug msg=" Lookaside configuration: using \"default-docker\" configuration"
time="2023-03-28T05:28:58Z" level=debug msg=" No signature storage configuration found for ghcr.io/linuxserver/hedgedoc:latest, using built-in default file:///var/lib/containers/sigstore"
time="2023-03-28T05:28:58Z" level=debug msg="Looking for TLS certificates and private keys in /etc/docker/certs.d/ghcr.io"
time="2023-03-28T05:28:58Z" level=debug msg=" Sigstore attachments: using \"default-docker\" configuration"
time="2023-03-28T05:28:58Z" level=debug msg="GET https://ghcr.io/v2/"
time="2023-03-28T05:28:58Z" level=debug msg="Ping https://ghcr.io/v2/ status 401"
time="2023-03-28T05:28:58Z" level=debug msg="GET https://ghcr.io/token?scope=repository%3Alinuxserver%2Fhedgedoc%3Apull&service=ghcr.io"
time="2023-03-28T05:28:58Z" level=debug msg="Increasing token expiration to: 60 seconds"
time="2023-03-28T05:28:58Z" level=debug msg="GET https://ghcr.io/v2/linuxserver/hedgedoc/tags/list"
time="2023-03-28T05:28:59Z" level=debug msg="GET https://ghcr.io/v2/linuxserver/hedgedoc/tags/list?last=amd64-version-1.8.1&n=0"
time="2023-03-28T05:28:59Z" level=debug msg="GET https://ghcr.io/v2/linuxserver/hedgedoc/tags/list?last=arm32v7-1.9.1&n=0"
time="2023-03-28T05:28:59Z" level=debug msg="GET https://ghcr.io/v2/linuxserver/hedgedoc/tags/list?last=1.9.3-ls56&n=0"
time="2023-03-28T05:28:59Z" level=debug msg="GET https://ghcr.io/v2/linuxserver/hedgedoc/tags/list?last=1.9.5-ls73&n=0"
{
    "Repository": "ghcr.io/linuxserver/hedgedoc",
    "Tags": [
        "amd64-1.7.0-ls1",
        "arm32v7-1.7.0-ls1",
        "arm64v8-1.7.0-ls1",
        "amd64-version-1.7.0",
```

## 認証情報が必要か確認する

まず `https://ghcr.io/v2/`にアクセスし、401をもらいます

HTTPのレスポンスヘッダの情報を参照すると・・

```
www-authenticate: Bearer realm="https://ghcr.io/token",service="ghcr.io",scope="repository:user/image:pull"
```
DockerHubの場合と違いscopeの記載があります

## アクセス用のトークンの取得

次のURLにアクセスしトークンを取得します `https://ghcr.io/token?scope=repository%3Alinuxserver%2Fhedgedoc%3Apull&service=ghcr.io`

- scope
    - repository/linuxserver/hedgedoc:pull
    - 上記で得たscopeを使わず、リポジトリ単位のスコープを取得している
- service
    - ghcr.io
```
curl "https://ghcr.io/token?scope=repository%3Alinuxserver%2Fhedgedoc%3Apull&service=ghcr.io" |jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    73  100    73    0     0    264      0 --:--:-- --:--:-- --:--:--   263
{
  "token": ...
}
```
- DockerHubのものとはレスポンスが違い、こちらはtokenというキーのみが入っています
- Docker Registry v2 authentication via central serviceの仕様を見ると`access_token`など他のパラメータはoptionalのようなので、これも仕様どおりの振る舞いです

この後`Increasing token expiration to: 60 seconds`というログが出ており、トークンの使用期限を伸ばしているようでしたが、この操作をせずとも次のコマンドが動作したので、深く動作を追っていません。

## 取得したトークンを使いタグ一覧を取得する 

`https://ghcr.io/v2/linuxserver/hedgedoc/tags/list`を呼び出してタグの一覧を取得します。

```
$ curl -H "Authorization: Bearer djE6bGludXhzZXJ2ZXIvaGVkZ2Vkb2M6MTY3OTk4MTkzNDQwODE1MzI0OA==" -v "https://ghcr.io/v2/linuxserver/hedgedoc/tags/list?n=0"|jq
...
> GET /v2/linuxserver/hedgedoc/tags/list?n=0 HTTP/2
> Host: ghcr.io
> User-Agent: curl/7.64.1
> Accept: */*
> Authorization: Bearer djE6bGludXhzZXJ2ZXIvaGVkZ2Vkb2M6MTY3OTk4MTkzNDQwODE1MzI0OA==
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
< content-type: application/json
< docker-distribution-api-version: registry/2.0
< link: </v2/linuxserver/hedgedoc/tags/list?last=amd64-version-1.8.1&n=0>; rel="next"
< date: Tue, 28 Mar 2023 05:41:54 GMT
< content-length: 1908
< x-github-request-id: 0539:3FEC:59AD6:B0ACD:64227E21
<
{ [1119 bytes data]
100  1908  100  1908    0     0   5695      0 --:--:-- --:--:-- --:--:--  5695
* Connection #0 to host ghcr.io left intact
* Closing connection 0
{
  "name": "linuxserver/hedgedoc",
  "tags": [
    "amd64-1.7.0-ls1",
    "arm32v7-1.7.0-ls1",
    "arm64v8-1.7.0-ls1",
```

`n`パラメータの指定がないときは100件のようです。

また、レスポンスヘッダの`link`の指定を見ると`n=0`というパラメータが付いており、これはnの指定がないものと同じ意味のようでした。（おそらく`n=100`でも同じ）

若干不気味ですが、次々とlinkヘッダが続く限りアクセスすることで、すべてのタグを取得しています。

- `https://ghcr.io/v2/linuxserver/hedgedoc/tags/list`
- `https://ghcr.io/v2/linuxserver/hedgedoc/tags/list?last=amd64-version-1.8.1&n=0`
- `https://ghcr.io/v2/linuxserver/hedgedoc/tags/list?last=arm32v7-1.9.1&n=0`
- `https://ghcr.io/v2/linuxserver/hedgedoc/tags/list?last=1.9.3-ls56&n=0`
- `https://ghcr.io/v2/linuxserver/hedgedoc/tags/list?last=1.9.5-ls73&n=0`

## GitHub Container Registryの独自API

GitHub Container RegistryはGitHubのPackagesという枠組みの機能の１つであるため、GitHubのpackagesのAPIで操作することが可能です。

https://docs.github.com/en/rest/packages?apiVersion=2022-11-28

エントリポイントは`https://api.github.com/orgs/<ORG>/packages`のようです。
Docker Registry HTTP API V2とはドメインが違います。

# Quay.io

前２つと同じか・・？と思いきやこれまた少し挙動が違います。

```
$ docker run --rm quay.io/skopeo/stable:v1.9.2 list-tags --debug docker://quay.io/ansible/creator-ee
time="2023-03-28T05:48:29Z" level=debug msg="Using registries.d directory /etc/containers/registries.d"
time="2023-03-28T05:48:29Z" level=debug msg="Loading registries configuration \"/etc/containers/registries.conf\""
time="2023-03-28T05:48:29Z" level=debug msg="Loading registries configuration \"/etc/containers/registries.conf.d/000-shortnames.conf\""
time="2023-03-28T05:48:29Z" level=debug msg="No credentials matching quay.io/ansible/creator-ee found in /tmp/auth.json"
time="2023-03-28T05:48:29Z" level=debug msg="No credentials matching quay.io/ansible/creator-ee found in /root/.config/containers/auth.json"
time="2023-03-28T05:48:29Z" level=debug msg="No credentials matching quay.io/ansible/creator-ee found in /root/.docker/config.json"
time="2023-03-28T05:48:29Z" level=debug msg="No credentials matching quay.io/ansible/creator-ee found in /root/.dockercfg"
time="2023-03-28T05:48:29Z" level=debug msg="No credentials for quay.io/ansible/creator-ee found"
time="2023-03-28T05:48:29Z" level=debug msg=" Lookaside configuration: using \"default-docker\" configuration"
time="2023-03-28T05:48:29Z" level=debug msg=" No signature storage configuration found for quay.io/ansible/creator-ee:latest, using built-in default file:///var/lib/containers/sigstore"
time="2023-03-28T05:48:29Z" level=debug msg="Looking for TLS certificates and private keys in /etc/docker/certs.d/quay.io"
time="2023-03-28T05:48:29Z" level=debug msg=" Sigstore attachments: using \"default-docker\" configuration"
time="2023-03-28T05:48:29Z" level=debug msg="GET https://quay.io/v2/"
time="2023-03-28T05:48:30Z" level=debug msg="Ping https://quay.io/v2/ status 401"
time="2023-03-28T05:48:30Z" level=debug msg="GET https://quay.io/v2/auth?scope=repository%3Aansible%2Fcreator-ee%3Apull&service=quay.io"
time="2023-03-28T05:48:31Z" level=debug msg="Increasing token expiration to: 60 seconds"
time="2023-03-28T05:48:31Z" level=debug msg="GET https://quay.io/v2/ansible/creator-ee/tags/list"
time="2023-03-28T05:48:32Z" level=debug msg="GET https://quay.io/v2/ansible/creator-ee/tags/list?n=50&next_page=<謎のハッシュ値>"
{
    "Repository": "quay.io/ansible/creator-ee",
    "Tags": [
        "8f2ef3e55d91258eca4eaef9222505bd401ff86d-1473632019-15-1",
        "v0.2.0a1",
```

## 認証情報が必要か確認する

```
$ curl -v https://quay.io/v2/
...
< HTTP/2 401
< date: Tue, 28 Mar 2023 05:49:53 GMT
< content-type: text/html; charset=utf-8
< content-length: 4
< server: nginx/1.20.1
< www-authenticate: Bearer realm="https://quay.io/v2/auth",service="quay.io"
< docker-distribution-api-version: registry/2.0
<
* Connection #0 to host quay.io left intact
true* Closing connection 0
```

他と同様に401が返却され、認証に必要な情報が返却されました。

こちらはDockerHubと同様にscopeの情報は有りません

## 認証トークンの取得

```
$ curl -v "https://quay.io/v2/auth?scope=repository%3Aansible%2Fcreator-ee%3Apull&service=quay.io"|jq
...
< HTTP/2 200
< date: Tue, 28 Mar 2023 05:51:37 GMT
< content-type: application/json
< content-length: 841
< server: nginx/1.20.1
< cache-control: no-cache, no-store, must-revalidate
< x-frame-options: DENY
< strict-transport-security: max-age=63072000; preload
<
{ [841 bytes data]
100   841  100   841    0     0   1086      0 --:--:-- --:--:-- --:--:--  1085
* Connection #0 to host quay.io left intact
* Closing connection 0
{
  "token": "..."
}
```

- scope
    - repository/ansible.creator-ee:pull
- service
    - quay.io


返却されるjsonのキーはtokenのみで、これはGitHub Container Registryと同じような振る舞いです。

この後`Increasing token expiration to: 60 seconds`というログが出ており、トークンの使用期限を伸ばしているようでしたが、この操作をせずとも次のコマンドが動作したので、深く動作を追っていません。

## 取得したトークンを使いタグ一覧を取得する

```
$ curl -H "Authorization: Bearer <上記で取得したtoken>" -v "https://quay.io/v2/ansible/creator-ee/tags/list"|jq
...
< server: nginx/1.20.1
< link: </v2/ansible/creator-ee/tags/list?n=50&next_page=gAAAAABkIoEP_XSQJENT2gJCiC_jnL4WZFxUQv2mDs7WRBF_21b-Lg5erskE1MWMUmfv5fdCP3o8JqmQEMhmlylx8edelVqeND299G2ZvCxaCPh5McC-0Xg%3D>; rel="next"
< x-frame-options: DENY
< strict-transport-security: max-age=63072000; preload
<
{ [1885 bytes data]
100  1885  100  1885    0     0   2026      0 --:--:-- --:--:-- --:--:--  2024
* Connection #0 to host quay.io left intact
* Closing connection 0
{
  "name": "ansible/creator-ee",
  "tags": [
    "8f2ef3e55d91258eca4eaef9222505bd401ff86d-1473632019-15-1",
    "v0.2.0a1",
    "8f2ef3e55d91258eca4eaef9222505bd401ff86d-1477382381-16-1",
    "8f2ef3e55d91258eca4eaef9222505bd401ff86d",
    "v0.2.0",
    "67b9e06a5462acd665a9260f5082f39f07fc2ef2-1968203497-17-1",
    "67b9e06a5462acd665a9260f5082f39f07fc2ef2",
    "v0.3.0",
    "52d2ac2718da1fe739a657e73a6094da18227ecb-2005081881-18-1",
    "52d2ac2718da1fe739a657e73a6094da18227ecb",
    "v0.3.1",
...
```

デフォルトの件数は50件のようで、`n`を増やしても50件以上は取得できませんでした。

`link`ヘッダの値を利用して次のページを取得するのですが、ここの値の様子が他のコンテナレジストリと大きく違いました。`n`パラメータは50で、これは違和感がありませんが、`last`ではなく`next_page`というパラメータ名がついており、その値も最後のtagの名前ではなく謎のハッシュ値となっています。

まぁ`link`ヘッダの中身をそのままリクエストすれば次のページの情報が得られるというのは同じなので、そういう処理を書いていれば問題はないのですが、ちょっと不思議な実装です。

ということで以下のURLで次ページを呼び出していました。

- `https://quay.io/v2/ansible/creator-ee/tags/list?n=50&next_page=<謎のハッシュ値>`

## Quayの独自API

Quayの独自のAPIもあります

https://docs.quay.io/api/

`https://quay.io/api/v1/`がエンドポイントです。

（Docker Registry HTTP API V2が`https://quay.io/v2`なので、並んでいるとややこしいですが、独立したAPIです）


## 実は認証情報は不要

色々試していて気づいたのですが、実はQuay.ioは公開リポジトリに関しては認証トークンなしでもアクセスできました。

```
$ curl  -v "https://quay.io/v2/ansible/creator-ee/tags/list"|jq
...
< HTTP/2 200
< date: Tue, 28 Mar 2023 06:03:47 GMT
< content-type: application/json
< content-length: 1885
< server: nginx/1.20.1
< link: </v2/ansible/creator-ee/tags/list?n=50&next_page=gAAAAABkIoNDsbasB-22vQs84yeAQtwR_OWxNscrx43fnYYUZ7T5lN0lGdy0xGzBU8P_mAVNwTHYVvRpjKmvkKQPcgRYUrOLNNKL9JCieJrtwBlUA-Dt5EM%3D>; rel="next"
< x-frame-options: DENY
< strict-transport-security: max-age=63072000; preload
<
{ [1885 bytes data]
100  1885  100  1885    0     0   2607      0 --:--:-- --:--:-- --:--:--  2607
* Connection #0 to host quay.io left intact
* Closing connection 0
{
  "name": "ansible/creator-ee",
  "tags": [
    "8f2ef3e55d91258eca4eaef9222505bd401ff86d-1473632019-15-1",
    "v0.2.0a1",
    "8f2ef3e55d91258eca4eaef9222505bd401ff86d-1477382381-16-1",
    "8f2ef3e55d91258eca4eaef9222505bd401ff86d",
    "v0.2.0",
...
```

# まとめ

コンテナレジストリの種類を問わず利用できるDocker Registry HTTP API V2は便利ですが、コンテナレジストリごとに微妙に振る舞いが違うので、そのあたりを考慮して実装しないとおかしなことになるということがわかりました。