docker_registry2 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +9 -0
- data/README.md +125 -0
- data/docker_registry2.gemspec +25 -0
- data/lib/docker_registry2.rb +18 -0
- data/lib/registry/exceptions.rb +23 -0
- data/lib/registry/registry.rb +101 -0
- data/lib/registry/version.rb +3 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c1566198b0c293c0e466c50b69a93087d19aa4d5
|
4
|
+
data.tar.gz: 785c90f7876ef4eed0afe80a7fc1150d8e30b325
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a79f5c43888ec5484e93c3a6e363f90d744daf6eefa6321046d8f7ca69b5e50575f992a6e29d844d14d429eb08c48d6b22c68bdac49fd6b60e92723dd56068a8
|
7
|
+
data.tar.gz: d3e2a3ca2d227f8deb22e77621f6ff94fed058ccaa3872ac77e08d9e43fc450f1e2cff0862c95111f541dbc418c56fff48ee51d37cbcb998a67d0d2c44bd9848
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
docker_registry2*.gem
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# Docker Registry
|
2
|
+
|
3
|
+
## Introduction
|
4
|
+
|
5
|
+
This is a simple gem that provides direct http access to a docker registry v2 without going through a docker server. You do **not** requires docker installed on your system to provide access.
|
6
|
+
|
7
|
+
````ruby
|
8
|
+
reg = DockerRegistry.new("https://my.registy.corp.com")
|
9
|
+
repos = reg.search("foo/repo")
|
10
|
+
tags = reg.tags("foo/repo")
|
11
|
+
````
|
12
|
+
|
13
|
+
Supports anonymous access, http authorization and v2 token access.
|
14
|
+
|
15
|
+
Inspired by https://github.com/rosylilly/docker_registry but written separately.
|
16
|
+
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add the following to your Gemfile:
|
21
|
+
|
22
|
+
gem 'docker_registry2`
|
23
|
+
|
24
|
+
And execute:
|
25
|
+
|
26
|
+
bundle install
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
Once it is installed, you first *open* a connection to a registry, and then *request* from the registry.
|
31
|
+
|
32
|
+
### Connecting
|
33
|
+
|
34
|
+
#### Anonymous
|
35
|
+
To connect to a registry:
|
36
|
+
|
37
|
+
````ruby
|
38
|
+
reg = DockerRegistry.new("https://my.registy.corp.com")
|
39
|
+
````
|
40
|
+
|
41
|
+
The above will connect anonymously to the registry via the endpoint `https://my.registry.corp.com/v2/`.
|
42
|
+
|
43
|
+
The following exceptions are thrown:
|
44
|
+
|
45
|
+
* `RegistryAuthenticationException`: registry does not support anonymous access
|
46
|
+
* `RegistryUnknownException`: registry does not exist at the given URL
|
47
|
+
* `RegistrySSLException`: registry SSL certificate cannot be validated
|
48
|
+
|
49
|
+
#### Authenticated
|
50
|
+
If you wish to authenticate, pass a username and password as the second and third parameters.
|
51
|
+
|
52
|
+
````ruby
|
53
|
+
reg = DockerRegistry.connect("https://myuser:mypass@my.registy.corp.com")
|
54
|
+
````
|
55
|
+
|
56
|
+
The following exceptions are thrown:
|
57
|
+
|
58
|
+
* `RegistryAuthenticationException`: username/password combination is invalid
|
59
|
+
* `RegistryAuthorizationException`: username/password does not have sufficient rights to access this registry
|
60
|
+
* `RegistryUnknownException`: registry does not exist at the given URL
|
61
|
+
* `RegistrySSLException`: registry SSL certificate cannot be validated
|
62
|
+
|
63
|
+
|
64
|
+
### Requests
|
65
|
+
Once you have a valid `reg` object return by `DockerRegistry.new()`, you can make requests. As of this version, only search and tags are supported. Others will be added over time.
|
66
|
+
|
67
|
+
|
68
|
+
#### search
|
69
|
+
````ruby
|
70
|
+
results = reg.search("mylibs")
|
71
|
+
````
|
72
|
+
|
73
|
+
Returns all repositories whose name contains `"mylibs"`.
|
74
|
+
|
75
|
+
**Note:** The v2 registry does not support search directly server-side. Thus, this is simulated by using the `catalog/` endpoint. It is highly recommended to avoid using this function until the v2 registry supports direct search, as it will be slow. It pulls a list of all repositories to the client and then does a pattern match on them.
|
76
|
+
|
77
|
+
Returns an array of strings, each of which is the full name of a repository.
|
78
|
+
|
79
|
+
If no results are found, will return an empty array `[]`. An empty array will not throw an exception.
|
80
|
+
|
81
|
+
The following exceptions are thrown:
|
82
|
+
|
83
|
+
* `RegistryAuthenticationException`: username and password are invalid
|
84
|
+
* `RegistryAuthorizationException`: user does not have sufficient rights to search in this registry
|
85
|
+
|
86
|
+
**NOTE:** The search endpoint relies on the catalog endpoint, which only is available from registry:2.1. If you try it prior to 2.1, you will get a `404` error.
|
87
|
+
|
88
|
+
#### tags
|
89
|
+
````ruby
|
90
|
+
results = reg.tags("mylibs")
|
91
|
+
````
|
92
|
+
|
93
|
+
Returns all known tags for the repository precisely named `"mylibs"`.
|
94
|
+
|
95
|
+
Returns an object with the following key value pairs:
|
96
|
+
array of objects, each of which has the following key/value pairs:
|
97
|
+
|
98
|
+
* `name`: full name of repository, e.g. `redis` or `user/redis`
|
99
|
+
* `tags`: array of strings, each of which is a tag for ths given repository
|
100
|
+
|
101
|
+
Other fields may be added later. Do *not* assume those are the only fields.
|
102
|
+
|
103
|
+
If no tags are found, or the named repository does not exist, return an empty object `{}`. An unknown repository will not throw an exception.
|
104
|
+
|
105
|
+
The following exceptions are thrown:
|
106
|
+
|
107
|
+
* `RegistryAuthenticationException`: username and password are invalid
|
108
|
+
* `RegistryAuthorizationException`: registry does not support tags using the given credentials, probably because the repository is private and the credentials provided do not have access
|
109
|
+
|
110
|
+
### Exceptions
|
111
|
+
|
112
|
+
All exceptions thrown inherit from `DockerRegistry::Exception`.
|
113
|
+
|
114
|
+
## License
|
115
|
+
|
116
|
+
MIT License.
|
117
|
+
|
118
|
+
## Contribution
|
119
|
+
|
120
|
+
Developed by Avi Deitcher http://github.com/deitch
|
121
|
+
Contributions courtesy of TraderTools, Inc. http://tradertools.com
|
122
|
+
|
123
|
+
|
124
|
+
|
125
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'registry/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'docker_registry2'
|
8
|
+
spec.version = DockerRegistry::VERSION
|
9
|
+
spec.authors = ['Avi Deitcher']
|
10
|
+
spec.summary = 'Docker v2 registry HTTP API client'
|
11
|
+
spec.description = 'Docker v2 registry HTTP API client with support for token authentication'
|
12
|
+
spec.homepage = 'https://github.com/deitch/docker_registry2'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
21
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
22
|
+
spec.add_development_dependency 'rubocop', '>= 0.26.0'
|
23
|
+
|
24
|
+
spec.add_dependency 'rest-client', '>= 1.8.0'
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/registry/version'
|
2
|
+
require File.dirname(__FILE__) + '/registry/registry'
|
3
|
+
require File.dirname(__FILE__) + '/registry/exceptions'
|
4
|
+
|
5
|
+
|
6
|
+
module DockerRegistry
|
7
|
+
def self.connect(uri)
|
8
|
+
@reg = DockerRegistry::Registry.new(uri)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.search(query = '')
|
12
|
+
@reg.search(query)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.tags(repository)
|
16
|
+
@reg.tags(repository)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module DockerRegistry
|
2
|
+
class Exception < RuntimeError
|
3
|
+
|
4
|
+
end
|
5
|
+
|
6
|
+
class RegistryAuthenticationException < Exception
|
7
|
+
end
|
8
|
+
|
9
|
+
class RegistryAuthorizationException < Exception
|
10
|
+
end
|
11
|
+
|
12
|
+
class RegistryUnknownException < Exception
|
13
|
+
end
|
14
|
+
|
15
|
+
class RegistrySSLException < Exception
|
16
|
+
end
|
17
|
+
|
18
|
+
class ReauthenticatedException < Exception
|
19
|
+
end
|
20
|
+
|
21
|
+
class UnknownRegistryException < Exception
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class DockerRegistry::Registry
|
5
|
+
# @param [#to_s] base_uri Docker registry base URI
|
6
|
+
# @param [Hash] options Client options
|
7
|
+
# @option options [#to_s] :user User name for basic authentication
|
8
|
+
# @option options [#to_s] :password Password for basic authentication
|
9
|
+
def initialize(uri, options = {})
|
10
|
+
@uri = URI.parse(uri)
|
11
|
+
@base_uri = "#{@uri.scheme}://#{@uri.host}:#{@uri.port}"
|
12
|
+
@user = @uri.user
|
13
|
+
@password = @uri.password
|
14
|
+
# make a ping connection
|
15
|
+
ping
|
16
|
+
end
|
17
|
+
|
18
|
+
def doget(url, token=nil)
|
19
|
+
begin
|
20
|
+
# do we already have a token to authenticate?
|
21
|
+
if token.nil?
|
22
|
+
response = RestClient.get @base_uri+url
|
23
|
+
else
|
24
|
+
response = RestClient.get @base_uri+url, Authorization: 'Bearer '+token
|
25
|
+
end
|
26
|
+
rescue SocketError
|
27
|
+
raise DockerRegistry::RegistryUnknownException
|
28
|
+
rescue RestClient::Unauthorized => e
|
29
|
+
# unauthorized
|
30
|
+
# did we already try for this realm and service and scope and have insufficient privileges?
|
31
|
+
if token.nil?
|
32
|
+
token = authenticate e.response.headers[:www_authenticate]
|
33
|
+
# go do whatever you were going to do again
|
34
|
+
response = doget url, token
|
35
|
+
else
|
36
|
+
throw DockerRegistry::RegistryAuthorizationException
|
37
|
+
end
|
38
|
+
rescue RestClient::ResourceNotFound
|
39
|
+
raise DockerRegistry::RegistryUnknownException
|
40
|
+
end
|
41
|
+
return response
|
42
|
+
end
|
43
|
+
|
44
|
+
def authenticate(header)
|
45
|
+
# get the parts we need
|
46
|
+
target = split_auth_header(header)
|
47
|
+
# did we have a username and password?
|
48
|
+
if defined? @user and @user.to_s.strip.length != 0
|
49
|
+
target[:params][:account] = @user
|
50
|
+
end
|
51
|
+
# authenticate against the realm
|
52
|
+
uri = URI.parse(target[:realm])
|
53
|
+
uri.user = @user if defined? @user
|
54
|
+
uri.password = @password if defined? @password
|
55
|
+
begin
|
56
|
+
response = RestClient.get uri.to_s, {params: target[:params]}
|
57
|
+
rescue RestClient::Unauthorized
|
58
|
+
# bad authentication
|
59
|
+
raise DockerRegistry::RegistryAuthenticationException
|
60
|
+
end
|
61
|
+
# now save the web token
|
62
|
+
return JSON.parse(response)["token"]
|
63
|
+
end
|
64
|
+
|
65
|
+
def split_auth_header(header = '')
|
66
|
+
h = Hash.new
|
67
|
+
h = {params: {}}
|
68
|
+
header.split(/[\s,]+/).each {|entry|
|
69
|
+
p = entry.split('=')
|
70
|
+
case p[0]
|
71
|
+
when 'Bearer'
|
72
|
+
when 'realm'
|
73
|
+
h[:realm] = p[1].gsub(/(^\"|\"$)/,'')
|
74
|
+
else
|
75
|
+
h[:params][p[0]] = p[1].gsub(/(^\"|\"$)/,'')
|
76
|
+
end
|
77
|
+
}
|
78
|
+
h
|
79
|
+
end
|
80
|
+
|
81
|
+
def ping
|
82
|
+
response = doget '/v2/'
|
83
|
+
end
|
84
|
+
|
85
|
+
def search(query = '')
|
86
|
+
response = doget "/v2/_catalog"
|
87
|
+
# parse the response
|
88
|
+
repos = JSON.parse(response)["repositories"]
|
89
|
+
if query.strip.length > 0
|
90
|
+
re = Regexp.new query
|
91
|
+
repos = repos.find_all {|e| re =~ e }
|
92
|
+
end
|
93
|
+
return repos
|
94
|
+
end
|
95
|
+
|
96
|
+
def tags(repo)
|
97
|
+
response = doget "/v2/#{repo}/tags/list"
|
98
|
+
# parse the response
|
99
|
+
JSON.parse response
|
100
|
+
end
|
101
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: docker_registry2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Avi Deitcher
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.26.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.26.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rest-client
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.8.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.8.0
|
69
|
+
description: Docker v2 registry HTTP API client with support for token authentication
|
70
|
+
email:
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- ".gitignore"
|
76
|
+
- Gemfile
|
77
|
+
- README.md
|
78
|
+
- docker_registry2.gemspec
|
79
|
+
- lib/docker_registry2.rb
|
80
|
+
- lib/registry/exceptions.rb
|
81
|
+
- lib/registry/registry.rb
|
82
|
+
- lib/registry/version.rb
|
83
|
+
homepage: https://github.com/deitch/docker_registry2
|
84
|
+
licenses:
|
85
|
+
- MIT
|
86
|
+
metadata: {}
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 2.4.5
|
104
|
+
signing_key:
|
105
|
+
specification_version: 4
|
106
|
+
summary: Docker v2 registry HTTP API client
|
107
|
+
test_files: []
|
108
|
+
has_rdoc:
|