tagfish 1.0.1 → 1.0.2
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 +4 -4
- data/Dockerfile +1 -1
- data/lib/tagfish/docker_registry_client.rb +24 -0
- data/lib/tagfish/docker_registry_v1_client.rb +54 -0
- data/lib/tagfish/docker_registry_v2_client.rb +59 -0
- data/lib/tagfish/docker_registry_vboth_client.rb +32 -0
- data/lib/tagfish/docker_uri.rb +0 -1
- data/lib/tagfish/search_command.rb +19 -12
- data/lib/tagfish/tags.rb +7 -9
- data/lib/tagfish/tags_command.rb +13 -16
- data/lib/tagfish/update/updater.rb +4 -4
- data/lib/tagfish/version.rb +1 -1
- metadata +7 -5
- data/lib/tagfish/docker_api.rb +0 -113
- data/lib/tagfish/tags_logic.rb +0 -52
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45ed3de3aea63c3fbb5108caa65a5e4f13fce18b
|
4
|
+
data.tar.gz: 58afb32647d0002ae0af805e7e13812f48b96220
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cfda8b965954cef8c83dd908ed315a067898a0e5143675a95fe6369c11ab64a83f19495c290a24432482b6179391fb6e673e75f9e1b0a2622f014c8cda42ae71
|
7
|
+
data.tar.gz: 9cf24c63a9a82178dc03a33918264993a5d3bcdfd6e254ef525da9fc5ec3fd16ef9da232e6409236533174a5eb2b2ea19c50b53daeda67a1edac917fb3376cf1
|
data/Dockerfile
CHANGED
@@ -11,7 +11,7 @@ RUN apk add --update ruby=2.2.3-r1 \
|
|
11
11
|
rm /var/cache/apk/* && \
|
12
12
|
rm -rf /usr/share/ri
|
13
13
|
|
14
|
-
RUN echo -e 'gem: --no-rdoc --no-ri' > /etc/gemrc \
|
14
|
+
RUN echo -e 'gem: --no-rdoc --no-ri' > /etc/gemrc && \
|
15
15
|
gem update --system 2.4.8 && \
|
16
16
|
gem install bundler -v 1.10.6 && \
|
17
17
|
rm -rf /usr/share/ri
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'tagfish/docker_registry_v1_client'
|
2
|
+
require 'tagfish/docker_registry_v2_client'
|
3
|
+
|
4
|
+
module Tagfish
|
5
|
+
module DockerRegistryClient
|
6
|
+
|
7
|
+
def self.for(*args)
|
8
|
+
[DockerRegistryV2Client, DockerRegistryV1Client].each do |client_class|
|
9
|
+
begin
|
10
|
+
return client_class.new(*args)
|
11
|
+
rescue APIVersionError
|
12
|
+
end
|
13
|
+
end
|
14
|
+
raise APIVersionError, "API version unrecognized!"
|
15
|
+
end
|
16
|
+
|
17
|
+
class AuthenticationError < StandardError
|
18
|
+
end
|
19
|
+
|
20
|
+
class APIVersionError < StandardError
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'tagfish/docker_registry_vboth_client'
|
2
|
+
|
3
|
+
module Tagfish
|
4
|
+
class DockerRegistryV1Client < DockerRegistryVbothClient
|
5
|
+
|
6
|
+
def api_version
|
7
|
+
'v1'
|
8
|
+
end
|
9
|
+
|
10
|
+
def search(keyword)
|
11
|
+
APICall.new(search_uri(keyword)).get_json(http_auth)
|
12
|
+
end
|
13
|
+
|
14
|
+
def tag_names
|
15
|
+
tag_map.tag_names
|
16
|
+
end
|
17
|
+
|
18
|
+
def tag_map
|
19
|
+
tags_list = tags_api(tags)
|
20
|
+
Tagfish::Tags.new(tags_list)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def tags
|
26
|
+
APICall.new(tags_uri).get_json(http_auth)
|
27
|
+
end
|
28
|
+
|
29
|
+
def tags_api(api_response_data)
|
30
|
+
case api_response_data
|
31
|
+
when Hash
|
32
|
+
api_response_data
|
33
|
+
when Array
|
34
|
+
api_response_data.reduce({}) do |images, tag|
|
35
|
+
images.merge({tag["name"] => tag["layer"]})
|
36
|
+
end
|
37
|
+
else
|
38
|
+
raise "unexpected type #{api_response_data.class}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def ping_uri
|
43
|
+
"#{base_uri}/v1/_ping"
|
44
|
+
end
|
45
|
+
|
46
|
+
def search_uri(keyword)
|
47
|
+
"#{base_uri}/v1/search?q=#{keyword}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def tags_uri
|
51
|
+
"#{base_uri}/v1/repositories/#{docker_uri.repository}/tags"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'tagfish/docker_registry_vboth_client'
|
2
|
+
|
3
|
+
module Tagfish
|
4
|
+
class DockerRegistryV2Client < DockerRegistryVbothClient
|
5
|
+
|
6
|
+
def api_version
|
7
|
+
'v2'
|
8
|
+
end
|
9
|
+
|
10
|
+
def catalog
|
11
|
+
APICall.new(catalog_uri).get_json(http_auth)
|
12
|
+
end
|
13
|
+
|
14
|
+
def tag_names
|
15
|
+
tags["tags"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def tag_map
|
19
|
+
Tagfish::Tags.new(tags_logic)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def tags
|
25
|
+
APICall.new(tags_uri).get_json(http_auth)
|
26
|
+
end
|
27
|
+
|
28
|
+
def hash(tag)
|
29
|
+
APICall.new(hash_uri(tag)).get_json(http_auth)
|
30
|
+
end
|
31
|
+
|
32
|
+
def tags_logic
|
33
|
+
if tag_names.nil?
|
34
|
+
abort("No Tags found for this repository")
|
35
|
+
end
|
36
|
+
|
37
|
+
tags_with_hashes = tag_names.inject({}) do |dict, tag|
|
38
|
+
dict[tag] = hash(tag)["fsLayers"]
|
39
|
+
dict
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def ping_uri
|
44
|
+
"#{base_uri}/v2/"
|
45
|
+
end
|
46
|
+
|
47
|
+
def catalog_uri
|
48
|
+
"#{base_uri}/v2/_catalog"
|
49
|
+
end
|
50
|
+
|
51
|
+
def tags_uri
|
52
|
+
"#{base_uri}/v2/#{docker_uri.repository}/tags/list"
|
53
|
+
end
|
54
|
+
|
55
|
+
def hash_uri(tag)
|
56
|
+
"#{base_uri}/v2/#{docker_uri.repository}/manifests/#{tag}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
require 'tagfish/docker_uri'
|
4
|
+
require 'tagfish/api_call'
|
5
|
+
require 'tagfish/tags'
|
6
|
+
|
7
|
+
module Tagfish
|
8
|
+
class DockerRegistryVbothClient
|
9
|
+
|
10
|
+
attr_accessor :docker_uri
|
11
|
+
attr_accessor :http_auth
|
12
|
+
|
13
|
+
def initialize(docker_uri)
|
14
|
+
@docker_uri = docker_uri
|
15
|
+
code = APICall.new(ping_uri).response_code
|
16
|
+
if code == 401
|
17
|
+
@http_auth = DockerHttpAuth.new(docker_uri.registry)
|
18
|
+
code = APICall.new(ping_uri).response_code(http_auth)
|
19
|
+
end
|
20
|
+
if code == 401
|
21
|
+
raise DockerRegistryClient::AuthenticationError, "Please `docker login <REGISTRY>` and try again"
|
22
|
+
elsif code != 200
|
23
|
+
raise DockerRegistryClient::APIVersionError, "Not recognized"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def base_uri
|
28
|
+
"#{docker_uri.protocol}#{docker_uri.registry}"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
data/lib/tagfish/docker_uri.rb
CHANGED
@@ -14,19 +14,26 @@ module Tagfish
|
|
14
14
|
docker_api = DockerAPI.new(docker_uri)
|
15
15
|
|
16
16
|
if docker_api.api_version == 'v2'
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
repos.each {|repo| puts "#{docker_uri.registry}/#{repo}"}
|
22
|
-
else # 'v1'
|
23
|
-
if not keyword
|
24
|
-
abort("You need to specify a keyword to search a Registry V1")
|
25
|
-
end
|
26
|
-
repos_raw = docker_api.search_v1(keyword)
|
27
|
-
repos = repos_raw["results"].map {|result| result["name"]}
|
28
|
-
puts repos
|
17
|
+
puts search_v2(keyword, docker_api, docker_uri)
|
18
|
+
else
|
19
|
+
puts search_v1(keyword, docker_api)
|
29
20
|
end
|
30
21
|
end
|
22
|
+
|
23
|
+
def search_v2(keyword, docker_api, docker_uri)
|
24
|
+
repos = docker_api.catalog_v2["repositories"]
|
25
|
+
if keyword
|
26
|
+
repos.select! {|repo| repo.include? keyword}
|
27
|
+
end
|
28
|
+
repos.map {|repo| "#{docker_uri.registry}/#{repo}"}
|
29
|
+
end
|
30
|
+
|
31
|
+
def search_v1(keyword, docker_api)
|
32
|
+
if not keyword
|
33
|
+
abort("You need to specify a keyword to search a Registry V1")
|
34
|
+
end
|
35
|
+
repos_raw = docker_api.search_v1(keyword)
|
36
|
+
repos = repos_raw["results"].map {|result| result["name"]}
|
37
|
+
end
|
31
38
|
end
|
32
39
|
end
|
data/lib/tagfish/tags.rb
CHANGED
@@ -4,23 +4,21 @@ require 'json'
|
|
4
4
|
module Tagfish
|
5
5
|
class Tags
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
attr_reader :tag_map
|
8
|
+
|
9
|
+
def initialize(tag_map)
|
10
|
+
@tag_map = tag_map
|
9
11
|
end
|
10
12
|
|
11
13
|
def tag_names
|
12
|
-
|
14
|
+
tag_map.keys.sort
|
13
15
|
end
|
14
16
|
|
15
17
|
def latest_tag
|
16
|
-
tag_names.
|
17
|
-
(
|
18
|
+
tag_names.detect do |tag_name|
|
19
|
+
(tag_map[tag_name] == tag_map["latest"]) && (tag_name != "latest")
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
-
def latest_tag_to_s
|
22
|
-
latest_tag.empty? ? nil : latest_tag[0]
|
23
|
-
end
|
24
|
-
|
25
23
|
end
|
26
24
|
end
|
data/lib/tagfish/tags_command.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require "tagfish/tags_logic"
|
2
1
|
require "tagfish/docker_uri"
|
2
|
+
require "tagfish/docker_registry_client"
|
3
3
|
|
4
4
|
module Tagfish
|
5
5
|
class TagsCommand < Clamp::Command
|
@@ -8,23 +8,20 @@ module Tagfish
|
|
8
8
|
option ["-s", "--short"], :flag, "only return tag, not full image path"
|
9
9
|
|
10
10
|
def execute
|
11
|
-
tags_only = latest? ? false : true
|
12
11
|
|
13
12
|
docker_uri = DockerURI.parse(repository)
|
14
|
-
docker_api =
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
"only `latest` tag available."
|
27
|
-
return
|
13
|
+
docker_api = DockerRegistryClient.for(docker_uri)
|
14
|
+
|
15
|
+
if latest?
|
16
|
+
tags = docker_api.tag_map
|
17
|
+
latest_tag = tags.latest_tag
|
18
|
+
if latest_tag.nil?
|
19
|
+
signal_error "No image explicitly tagged in this Repository, " +
|
20
|
+
"only `latest` tag available."
|
21
|
+
end
|
22
|
+
tags_found = [latest_tag]
|
23
|
+
else
|
24
|
+
tags_found = docker_api.tag_names
|
28
25
|
end
|
29
26
|
|
30
27
|
pretty_tags = tags_found.map do |tag_name|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'tagfish/tokeniser'
|
2
2
|
require 'tagfish/docker_uri'
|
3
|
-
require 'tagfish/
|
3
|
+
require 'tagfish/docker_registry_client'
|
4
4
|
|
5
5
|
module Tagfish
|
6
6
|
module Update
|
@@ -38,9 +38,9 @@ module Tagfish
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def update_uri(docker_uri)
|
41
|
-
docker_api =
|
42
|
-
tags =
|
43
|
-
newest_tag_name = tags.
|
41
|
+
docker_api = DockerRegistryClient.for(docker_uri)
|
42
|
+
tags = docker_api.tag_map
|
43
|
+
newest_tag_name = tags.latest_tag
|
44
44
|
if newest_tag_name.nil?
|
45
45
|
docker_uri
|
46
46
|
else
|
data/lib/tagfish/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tagfish
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Clement Labbe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -110,13 +110,15 @@ files:
|
|
110
110
|
- bin/tagfish
|
111
111
|
- lib/tagfish.rb
|
112
112
|
- lib/tagfish/api_call.rb
|
113
|
-
- lib/tagfish/docker_api.rb
|
114
113
|
- lib/tagfish/docker_http_auth.rb
|
114
|
+
- lib/tagfish/docker_registry_client.rb
|
115
|
+
- lib/tagfish/docker_registry_v1_client.rb
|
116
|
+
- lib/tagfish/docker_registry_v2_client.rb
|
117
|
+
- lib/tagfish/docker_registry_vboth_client.rb
|
115
118
|
- lib/tagfish/docker_uri.rb
|
116
119
|
- lib/tagfish/search_command.rb
|
117
120
|
- lib/tagfish/tags.rb
|
118
121
|
- lib/tagfish/tags_command.rb
|
119
|
-
- lib/tagfish/tags_logic.rb
|
120
122
|
- lib/tagfish/tokeniser.rb
|
121
123
|
- lib/tagfish/update/differ.rb
|
122
124
|
- lib/tagfish/update/update_command.rb
|
@@ -145,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
147
|
version: '0'
|
146
148
|
requirements: []
|
147
149
|
rubyforge_project:
|
148
|
-
rubygems_version: 2.
|
150
|
+
rubygems_version: 2.5.1
|
149
151
|
signing_key:
|
150
152
|
specification_version: 4
|
151
153
|
summary: Command line utility for docker registries
|
data/lib/tagfish/docker_api.rb
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
require 'json'
|
3
|
-
require 'tagfish/docker_uri'
|
4
|
-
require 'tagfish/api_call'
|
5
|
-
|
6
|
-
module Tagfish
|
7
|
-
class DockerAPI
|
8
|
-
|
9
|
-
attr_accessor :docker_uri
|
10
|
-
attr_accessor :api_version
|
11
|
-
attr_accessor :http_auth
|
12
|
-
|
13
|
-
def initialize(docker_uri)
|
14
|
-
@docker_uri = docker_uri
|
15
|
-
retrieve_api_version_and_auth()
|
16
|
-
end
|
17
|
-
|
18
|
-
def retrieve_api_version_and_auth
|
19
|
-
code = try_api('v1')
|
20
|
-
if code != 200
|
21
|
-
code = try_api('v2')
|
22
|
-
end
|
23
|
-
if code == 401
|
24
|
-
abort("Authentication failed, please `docker login <REGISTRY>` and try again.")
|
25
|
-
elsif code != 200
|
26
|
-
abort("API version not recognized")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def try_api(version)
|
31
|
-
code = APICall.new(ping_uri(version)).response_code
|
32
|
-
if code == 200
|
33
|
-
@api_version = version
|
34
|
-
elsif code == 401
|
35
|
-
code = init_auth(version)
|
36
|
-
if code == 200
|
37
|
-
@api_version = version
|
38
|
-
end
|
39
|
-
end
|
40
|
-
return code
|
41
|
-
end
|
42
|
-
|
43
|
-
def init_auth(api_version)
|
44
|
-
@http_auth = DockerHttpAuth.new(docker_uri.registry)
|
45
|
-
if api_version == 'v2'
|
46
|
-
code = APICall.new(ping_v2_uri).response_code(http_auth)
|
47
|
-
elsif api_version == 'v1'
|
48
|
-
code = APICall.new(ping_v1_uri).response_code(http_auth)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def tags_v1
|
53
|
-
APICall.new(tags_v1_uri).get_json(http_auth)
|
54
|
-
end
|
55
|
-
|
56
|
-
def tags_v2
|
57
|
-
APICall.new(tags_v2_uri).get_json(http_auth)
|
58
|
-
end
|
59
|
-
|
60
|
-
def hash_v2(tag)
|
61
|
-
APICall.new(hash_v2_uri(tag)).get_json(http_auth)
|
62
|
-
end
|
63
|
-
|
64
|
-
def catalog_v2
|
65
|
-
APICall.new(catalog_v2_uri).get_json(http_auth)
|
66
|
-
end
|
67
|
-
|
68
|
-
def search_v1(keyword)
|
69
|
-
APICall.new(search_v1_uri(keyword)).get_json(http_auth)
|
70
|
-
end
|
71
|
-
|
72
|
-
def base_uri
|
73
|
-
"#{docker_uri.protocol}#{docker_uri.registry}"
|
74
|
-
end
|
75
|
-
|
76
|
-
def ping_uri(version)
|
77
|
-
if version == 'v1'
|
78
|
-
ping_v1_uri
|
79
|
-
elsif version == 'v2'
|
80
|
-
ping_v2_uri
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def ping_v2_uri
|
85
|
-
"#{base_uri}/v2/"
|
86
|
-
end
|
87
|
-
|
88
|
-
def ping_v1_uri
|
89
|
-
"#{base_uri}/v1/_ping"
|
90
|
-
end
|
91
|
-
|
92
|
-
def catalog_v2_uri
|
93
|
-
"#{base_uri}/v2/_catalog"
|
94
|
-
end
|
95
|
-
|
96
|
-
def search_v1_uri(keyword)
|
97
|
-
"#{base_uri}/v1/search?q=#{keyword}"
|
98
|
-
end
|
99
|
-
|
100
|
-
def tags_v1_uri
|
101
|
-
"#{base_uri}/v1/repositories/#{docker_uri.repository}/tags"
|
102
|
-
end
|
103
|
-
|
104
|
-
def tags_v2_uri
|
105
|
-
"#{base_uri}/v2/#{docker_uri.repository}/tags/list"
|
106
|
-
end
|
107
|
-
|
108
|
-
def hash_v2_uri(tag)
|
109
|
-
"#{base_uri}/v2/#{docker_uri.repository}/manifests/#{tag}"
|
110
|
-
end
|
111
|
-
|
112
|
-
end
|
113
|
-
end
|
data/lib/tagfish/tags_logic.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'tagfish/tags'
|
3
|
-
|
4
|
-
module Tagfish
|
5
|
-
class TagsLogic
|
6
|
-
def self.find_tags_by_repository(docker_api, tags_only=false)
|
7
|
-
if docker_api.api_version == 'v2'
|
8
|
-
tags_list = tags_v2(docker_api, tags_only)
|
9
|
-
else
|
10
|
-
tags_list = tags_v1(docker_api)
|
11
|
-
end
|
12
|
-
Tagfish::Tags.new(tags_list)
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def self.tags_v1(docker_api)
|
18
|
-
tags_json = docker_api.tags_v1
|
19
|
-
tags_v1_api(tags_json)
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.tags_v1_api(api_response_data)
|
23
|
-
case api_response_data
|
24
|
-
when Hash
|
25
|
-
api_response_data
|
26
|
-
when Array
|
27
|
-
api_response_data.reduce({}) do |images, tag|
|
28
|
-
images.merge({tag["name"] => tag["layer"]})
|
29
|
-
end
|
30
|
-
else
|
31
|
-
raise "unexpected type #{api_response_data.class}"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.tags_v2(docker_api, tags_only)
|
36
|
-
tags = docker_api.tags_v2["tags"]
|
37
|
-
if tags.nil?
|
38
|
-
abort("No Tags found for this repository")
|
39
|
-
end
|
40
|
-
|
41
|
-
tags_with_hashes = tags.inject({}) do |dict, tag|
|
42
|
-
if tags_only
|
43
|
-
dict[tag] = "dummy_hash"
|
44
|
-
else
|
45
|
-
dict[tag] = docker_api.hash_v2(tag)["fsLayers"][0]["blobSum"]
|
46
|
-
end
|
47
|
-
dict
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|