Commit a8b46301 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'docker-registry-manifest-v1' into 'master'

Add support for Docker Registry manifest v1

## What does this MR do?

Adds support for Manifest V1 generated by older versions of Docker (before 1.10).

## What are the relevant issue numbers?

Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/18609


See merge request !4669
parents e27de526 3213023d
...@@ -31,6 +31,7 @@ v 8.9.0 (unreleased) ...@@ -31,6 +31,7 @@ v 8.9.0 (unreleased)
- Add Environments and Deployments - Add Environments and Deployments
- Redesign account and email confirmation emails - Redesign account and email confirmation emails
- Don't fail builds for projects that are deleted - Don't fail builds for projects that are deleted
- Support Docker Registry manifest v1
- `git clone https://host/namespace/project` now works, in addition to using the `.git` suffix - `git clone https://host/namespace/project` now works, in addition to using the `.git` suffix
- Bump nokogiri to 1.6.8 - Bump nokogiri to 1.6.8
- Use gitlab-shell v3.0.0 - Use gitlab-shell v3.0.0
......
...@@ -9,11 +9,19 @@ ...@@ -9,11 +9,19 @@
- else - else
\- \-
%td %td
= number_to_human_size(tag.total_size) - if tag.total_size
· = number_to_human_size(tag.total_size)
= pluralize(tag.layers.size, "layer") ·
= pluralize(tag.layers.size, "layer")
- else
.light
\-
%td %td
= time_ago_in_words(tag.created_at) - if tag.created_at
= time_ago_in_words(tag.created_at)
- else
.light
\-
- if can?(current_user, :update_container_image, @project) - if can?(current_user, :update_container_image, @project)
%td.content %td.content
.controls.hidden-xs.pull-right .controls.hidden-xs.pull-right
......
...@@ -18,7 +18,7 @@ module ContainerRegistry ...@@ -18,7 +18,7 @@ module ContainerRegistry
end end
def digest def digest
config['digest'] config['digest'] || config['blobSum']
end end
def type def type
......
...@@ -47,7 +47,9 @@ module ContainerRegistry ...@@ -47,7 +47,9 @@ module ContainerRegistry
conn.request :json conn.request :json
conn.headers['Accept'] = MANIFEST_VERSION conn.headers['Accept'] = MANIFEST_VERSION
conn.response :json, content_type: /\bjson$/ conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+prettyjws'
conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+json'
conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v2+json'
if options[:user] && options[:password] if options[:user] && options[:password]
conn.request(:basic_auth, options[:user].to_s, options[:password].to_s) conn.request(:basic_auth, options[:user].to_s, options[:password].to_s)
......
...@@ -12,6 +12,14 @@ module ContainerRegistry ...@@ -12,6 +12,14 @@ module ContainerRegistry
manifest.present? manifest.present?
end end
def v1?
manifest && manifest['schemaVersion'] == 1
end
def v2?
manifest && manifest['schemaVersion'] == 2
end
def manifest def manifest
return @manifest if defined?(@manifest) return @manifest if defined?(@manifest)
...@@ -57,7 +65,9 @@ module ContainerRegistry ...@@ -57,7 +65,9 @@ module ContainerRegistry
return @layers if defined?(@layers) return @layers if defined?(@layers)
return unless manifest return unless manifest
@layers = manifest['layers'].map do |layer| layers = manifest['layers'] || manifest['fsLayers']
@layers = layers.map do |layer|
repository.blob(layer) repository.blob(layer)
end end
end end
...@@ -65,7 +75,7 @@ module ContainerRegistry ...@@ -65,7 +75,7 @@ module ContainerRegistry
def total_size def total_size
return unless layers return unless layers
layers.map(&:size).sum layers.map(&:size).sum if v2?
end end
def delete def delete
......
{
"schemaVersion": 1,
"name": "library/alpine",
"tag": "2.6",
"architecture": "amd64",
"fsLayers": [
{
"blobSum": "sha256:2a3ebcb7fbcc29bf40c4f62863008bb573acdea963454834d9483b3e5300c45d"
}
],
"history": [
{
"v1Compatibility": "{\"id\":\"dd807873c9a21bcc82e30317c283e6601d7e19f5cf7867eec34cdd1aeb3f099e\",\"created\":\"2016-01-18T18:32:39.162138276Z\",\"container\":\"556a728876db7b0e621adc029c87c649d32520804f8f15defd67bb070dc1a88d\",\"container_config\":{\"Hostname\":\"556a728876db\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":null,\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) ADD file:7dee8a455bcc39013aa168d27ece9227aad155adbaacbd153d94ca60113f59fc in /\"],\"Image\":\"\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"docker_version\":\"1.8.3\",\"config\":{\"Hostname\":\"556a728876db\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":null,\"Cmd\":null,\"Image\":\"\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":null},\"architecture\":\"amd64\",\"os\":\"linux\",\"Size\":4501436}"
}
],
"signatures": [
{
"header": {
"jwk": {
"crv": "P-256",
"kid": "4MZL:Z5ZP:2RPA:Q3TD:QOHA:743L:EM2G:QY6Q:ZJCX:BSD7:CRYC:LQ6T",
"kty": "EC",
"x": "qmWOaxPUk7QsE5iTPdeG1e9yNE-wranvQEnWzz9FhWM",
"y": "WeeBpjTOYnTNrfCIxtFY5qMrJNNk9C1vc5ryxbbMD_M"
},
"alg": "ES256"
},
"signature": "0zmjTJ4m21yVwAeteLc3SsQ0miScViCDktFPR67W-ozGjjI3iBjlDjwOl6o2sds5ZI9U6bSIKOeLDinGOhHoOQ",
"protected": "eyJmb3JtYXRMZW5ndGgiOjEzNzIsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAxNi0wNi0xNVQxMDo0NDoxNFoifQ"
}
]
}
...@@ -17,46 +17,85 @@ describe ContainerRegistry::Tag do ...@@ -17,46 +17,85 @@ describe ContainerRegistry::Tag do
end end
context 'manifest processing' do context 'manifest processing' do
before do context 'schema v1' do
stub_request(:get, 'http://example.com/v2/group/test/manifests/tag'). before do
with(headers: headers). stub_request(:get, 'http://example.com/v2/group/test/manifests/tag').
to_return( with(headers: headers).
status: 200, to_return(
body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json'), status: 200,
headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' }) body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest_1.json'),
end headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v1+prettyjws' })
end
context '#layers' do context '#layers' do
subject { tag.layers } subject { tag.layers }
it { expect(subject.length).to eq(1) } it { expect(subject.length).to eq(1) }
end end
context '#total_size' do
subject { tag.total_size }
context '#total_size' do it { is_expected.to be_nil }
subject { tag.total_size } end
it { is_expected.to eq(2319870) } context 'config processing' do
context '#config' do
subject { tag.config }
it { is_expected.to be_nil }
end
context '#created_at' do
subject { tag.created_at }
it { is_expected.to be_nil }
end
end
end end
context 'config processing' do context 'schema v2' do
before do before do
stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac'). stub_request(:get, 'http://example.com/v2/group/test/manifests/tag').
with(headers: { 'Accept' => 'application/octet-stream' }). with(headers: headers).
to_return( to_return(
status: 200, status: 200,
body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json')) body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json'),
headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' })
end end
context '#config' do context '#layers' do
subject { tag.config } subject { tag.layers }
it { is_expected.not_to be_nil } it { expect(subject.length).to eq(1) }
end end
context '#created_at' do context '#total_size' do
subject { tag.created_at } subject { tag.total_size }
it { is_expected.to eq(2319870) }
end
context 'config processing' do
before do
stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
with(headers: { 'Accept' => 'application/octet-stream' }).
to_return(
status: 200,
body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
end
context '#config' do
subject { tag.config }
it { is_expected.not_to be_nil }
end
context '#created_at' do
subject { tag.created_at }
it { is_expected.not_to be_nil } it { is_expected.not_to be_nil }
end
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment