docker-registry-sync 0.1.5 → 0.2.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
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MjJjMmUyZWRiNWY1NjYzMDI1MTZiMjAxZDZhMWY5MDgzZjNjOTFiOA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
Nzc5MWQxNzhiZmUxOGU0NTRlNzEyYTU2MmEzODE5ODhiMTE3MDllMw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Yzc0MTExY2M3MjkxYTFlNzQxY2ExYjhiMTg5YTI0ODI1MTY4ZDdkNDA4YmQ2
|
10
|
+
MTA3ODNkMzU1ZWExODcyZTViNDExMjgzZGQ4ZjdjMDk1ZDQxNWE1YmFmNzI4
|
11
|
+
YWI2ZWVkN2VmM2NlZTYyMWExMmVjZDY3OGJiZTExY2Y3NGRkZjk=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NzIyMzM5OWY2ZDRkYTA2MmZlMDE5Y2ViYTZkYTE1YjE3NWVlMmFhY2RjNzZk
|
14
|
+
YTM1YjJhNWQxNzQ2NDEwMjU5ZTY2NjhiZjJmOWMyNWJkYmVmZGQzYzQ5ODAw
|
15
|
+
MjJkMmY4ZjZkNmQyZGFkOGFjMTBmYzRkNWEwNWNlOGE2ZDQyODU=
|
data/bin/docker-registry-sync
CHANGED
@@ -64,6 +64,12 @@ EOC
|
|
64
64
|
options[:source_sse] = true
|
65
65
|
end
|
66
66
|
|
67
|
+
options[:pool] = 5
|
68
|
+
opts.on('-n POOL', '--pool POOL', "Size of worker thread pool, defaults to 5.") do |t|
|
69
|
+
puts "pool: #{t}"
|
70
|
+
options[:pool] = t.to_i
|
71
|
+
end
|
72
|
+
|
67
73
|
options[:unset_proxy] = false
|
68
74
|
opts.on(nil, '--unset-proxy', "Use if 'http_proxy' is set in your environment, but you don't want to use it...") do |_u|
|
69
75
|
options[:unset_proxy] = true
|
@@ -96,6 +102,7 @@ unless options[:unset_proxy]
|
|
96
102
|
end
|
97
103
|
end
|
98
104
|
|
105
|
+
puts options, cmd
|
99
106
|
unless verify_opts(options, cmd)
|
100
107
|
puts "Both '--source-bucket' and '--target-buckets' must be defined for 'sync' and 'queue-sync' commands\n"
|
101
108
|
puts "'--queue' must be defined for 'queue-sync' and 'run-sync' commands\n\n"
|
@@ -108,7 +115,8 @@ CMD.configure(
|
|
108
115
|
options[:target_buckets],
|
109
116
|
options[:sqs_queue],
|
110
117
|
options[:sse],
|
111
|
-
options[:source_sse]
|
118
|
+
options[:source_sse],
|
119
|
+
options[:pool]
|
112
120
|
)
|
113
121
|
|
114
122
|
ec = 1
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'thread'
|
3
|
+
require 'monitor'
|
2
4
|
require 'docker/registry/sync/s3'
|
3
5
|
require 'docker/registry/sync/sqs'
|
4
6
|
|
@@ -9,7 +11,7 @@ module Docker
|
|
9
11
|
include Docker::Registry::Sync
|
10
12
|
|
11
13
|
class << self
|
12
|
-
def configure(source_bucket, target_buckets, sqs_queue, use_sse, source_uses_sse)
|
14
|
+
def configure(source_bucket, target_buckets, sqs_queue, use_sse, source_uses_sse, pool)
|
13
15
|
unless source_bucket.nil?
|
14
16
|
source_region, source_bucket = source_bucket.split(':')
|
15
17
|
else
|
@@ -27,6 +29,7 @@ module Docker
|
|
27
29
|
else
|
28
30
|
sqs_region, sqs_uri = nil, nil
|
29
31
|
end
|
32
|
+
|
30
33
|
Docker::Registry::Sync.configure do |config|
|
31
34
|
config.source_bucket = source_bucket
|
32
35
|
config.source_region = source_region
|
@@ -34,6 +37,7 @@ module Docker
|
|
34
37
|
config.source_sse = source_uses_sse
|
35
38
|
config.sse = use_sse
|
36
39
|
config.sqs_region = sqs_region
|
40
|
+
config.pool_size = pool
|
37
41
|
config.sqs_url = "https://#{sqs_uri}"
|
38
42
|
end
|
39
43
|
@config = Docker::Registry::Sync.config
|
@@ -44,15 +48,53 @@ module Docker
|
|
44
48
|
|
45
49
|
Signal.trap('INT') do
|
46
50
|
@config.logger.error 'Received INT signal...'
|
51
|
+
@producer_finished = true
|
47
52
|
@terminated = true
|
48
53
|
end
|
49
54
|
Signal.trap('TERM') do
|
50
55
|
@config.logger.error 'Received TERM signal...'
|
56
|
+
@producer_finished = true
|
51
57
|
@terminated = true
|
52
58
|
end
|
53
59
|
end
|
54
60
|
|
61
|
+
def start_workers
|
62
|
+
@threads = Array.new(@config.pool_size)
|
63
|
+
@work_queue = Queue.new
|
64
|
+
@status_queue = Queue.new
|
65
|
+
|
66
|
+
@threads.extend(MonitorMixin)
|
67
|
+
@threads_available = @threads.new_cond
|
68
|
+
|
69
|
+
@producer_finished = false
|
70
|
+
@consumer_thread = Thread.new do
|
71
|
+
sync_key_consumer
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def finalize_workers
|
76
|
+
return true
|
77
|
+
@producer_finished = true
|
78
|
+
@consumer_thread.join
|
79
|
+
@threads.each { |t| t.join unless t.nil? }
|
80
|
+
@config.logger.info "Processing job results..."
|
81
|
+
|
82
|
+
success = true
|
83
|
+
loop do
|
84
|
+
begin
|
85
|
+
# One job filure is a run failure
|
86
|
+
success &&= @status_queue.pop(true)
|
87
|
+
rescue ThreadError
|
88
|
+
@config.logger.info "Finished processing job results..."
|
89
|
+
break
|
90
|
+
end
|
91
|
+
end
|
92
|
+
success && !@terminated
|
93
|
+
end
|
94
|
+
|
55
95
|
def sync(image, tag)
|
96
|
+
configure_signal_handlers
|
97
|
+
start_workers
|
56
98
|
success = false
|
57
99
|
@config.target_buckets.each do |region, bucket, sse|
|
58
100
|
if image_exists?(image, bucket, region)
|
@@ -61,6 +103,7 @@ module Docker
|
|
61
103
|
success = sync_repo(image, bucket, region, !sse.nil?)
|
62
104
|
end
|
63
105
|
end
|
106
|
+
success &&= finalize_workers
|
64
107
|
success ? 0 : 1
|
65
108
|
end
|
66
109
|
|
@@ -85,13 +128,15 @@ module Docker
|
|
85
128
|
def run_sync
|
86
129
|
ec = 1
|
87
130
|
configure_signal_handlers
|
131
|
+
start_workers
|
88
132
|
begin
|
89
133
|
@config.logger.info 'Polling queue for images to sync...'
|
90
134
|
sqs = Aws::SQS::Client.new(region: @config.sqs_region)
|
91
135
|
resp = sqs.receive_message(
|
92
136
|
queue_url: @config.sqs_url,
|
93
137
|
max_number_of_messages: 1,
|
94
|
-
visibility_timeout: 900, # Give ourselves 15min to sync the image
|
138
|
+
#visibility_timeout: 900, # Give ourselves 15min to sync the image
|
139
|
+
visibility_timeout: 30, # Give ourselves 15min to sync the image
|
95
140
|
wait_time_seconds: 10, # Wait a maximum of 10s for a new message
|
96
141
|
)
|
97
142
|
@config.logger.info "SQS returned #{resp.messages.length} new images to sync..."
|
@@ -102,15 +147,20 @@ module Docker
|
|
102
147
|
|
103
148
|
if image_exists?(data['image'], data['target']['bucket'], data['target']['region'])
|
104
149
|
@config.logger.info("Syncing tag: #{data['image']}:#{data['tag']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
105
|
-
|
150
|
+
success = sync_tag(data['image'], data['tag'], data['target']['bucket'], data['target']['region'], data['target']['sse'], data['source']['bucket'], data['source']['region'])
|
151
|
+
success &&= finalize_workers
|
152
|
+
|
153
|
+
if success
|
106
154
|
@config.logger.info("Finished syncing tag: #{data['image']}:#{data['tag']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
107
155
|
finalize_message(message.receipt_handle)
|
108
156
|
else
|
109
157
|
@config.logger.info("Falied to sync tag, leaving on queue: #{data['image']}:#{data['tag']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
110
158
|
end
|
111
159
|
else
|
160
|
+
success = sync_repo(data['image'], data['target']['bucket'], data['target']['region'], data['target']['sse'], data['source']['bucket'], data['source']['region'])
|
161
|
+
success &&= finalize_workers
|
112
162
|
@config.logger.info("Syncing image: #{data['image']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
113
|
-
if
|
163
|
+
if success
|
114
164
|
@config.logger.info("Finished syncing image: #{data['image']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
115
165
|
finalize_message(message.receipt_handle)
|
116
166
|
else
|
@@ -53,7 +53,7 @@ module Docker
|
|
53
53
|
end
|
54
54
|
|
55
55
|
class Configuration
|
56
|
-
attr_accessor :source_bucket, :source_region, :target_buckets, :sqs_region, :sqs_url, :empty_queue_sleep_time, :sse, :source_sse
|
56
|
+
attr_accessor :source_bucket, :source_region, :target_buckets, :sqs_region, :sqs_url, :empty_queue_sleep_time, :sse, :source_sse, :pool_size
|
57
57
|
attr_accessor :log_level, :logger
|
58
58
|
|
59
59
|
def initialize
|
@@ -69,6 +69,7 @@ module Docker
|
|
69
69
|
@sse = false
|
70
70
|
|
71
71
|
@empty_queue_sleep_time = 5
|
72
|
+
@pool_size = 5
|
72
73
|
|
73
74
|
@log_level = :debug
|
74
75
|
@logger = Docker::Registry::Sync::STDLogger
|
@@ -36,6 +36,7 @@ module Docker
|
|
36
36
|
sync_image(img_id, bucket, region, sse, source_bucket, source_region)
|
37
37
|
rescue Exception => e
|
38
38
|
@config.logger.error "An unexpected error occoured while syncing tag #{image}:#{tag}: #{e}"
|
39
|
+
@config.logger.error e.backtrace
|
39
40
|
false
|
40
41
|
else
|
41
42
|
true
|
@@ -58,6 +59,7 @@ module Docker
|
|
58
59
|
end
|
59
60
|
rescue Exception => e
|
60
61
|
@config.logger.error "An unexpected error occoured while syncing repo #{repo}: #{e}"
|
62
|
+
@config.logger.error e.backtrace
|
61
63
|
false
|
62
64
|
else
|
63
65
|
true
|
@@ -99,6 +101,7 @@ module Docker
|
|
99
101
|
keys.each do |key|
|
100
102
|
@config.logger.info "Syncing key #{source_bucket}/#{key} to bucket #{target_bucket}"
|
101
103
|
opts = {acl: 'bucket-owner-full-control',
|
104
|
+
region: target_client.get_bucket_location(bucket: target_bucket).location_constraint,
|
102
105
|
bucket: target_bucket,
|
103
106
|
key: key,
|
104
107
|
copy_source: "#{source_bucket}/#{key}"}
|
@@ -108,7 +111,52 @@ module Docker
|
|
108
111
|
if @config.source_sse
|
109
112
|
opts[:copy_source_sse_customer_algorithm] = 'AES256'
|
110
113
|
end
|
111
|
-
|
114
|
+
@work_queue << opts
|
115
|
+
sleep 0.1
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def sync_key_consumer
|
120
|
+
@config.logger.info "Starting sync consumer..."
|
121
|
+
loop do
|
122
|
+
break if @producer_finished && @work_queue.length == 0
|
123
|
+
t_index = nil
|
124
|
+
|
125
|
+
begin
|
126
|
+
busy = @threads.select { |t| t.nil? || t.status == false || t['finished'].nil? == false }.length == 0
|
127
|
+
end until !busy
|
128
|
+
t_index = @threads.rindex { |t| t.nil? || t.status == false || t['finished'].nil? == false }
|
129
|
+
|
130
|
+
begin
|
131
|
+
opts = @work_queue.pop(true)
|
132
|
+
rescue ThreadError
|
133
|
+
@config.logger.info "No work found on the queue, sleeping..."
|
134
|
+
sleep 1
|
135
|
+
else
|
136
|
+
if opts[:key]
|
137
|
+
@threads[t_index].join unless @threads[t_index].nil?
|
138
|
+
@threads[t_index] = Thread.new do
|
139
|
+
@config.logger.info "Worker syncing key: #{opts[:key]}"
|
140
|
+
target_client = Aws::S3::Client.new(region: opts[:region])
|
141
|
+
opts.delete :region
|
142
|
+
success = false
|
143
|
+
begin
|
144
|
+
target_client.copy_object(opts)
|
145
|
+
success = true
|
146
|
+
rescue Exception => e
|
147
|
+
@config.logger.error "An unknown error occoured while copying object in s3: #{e}"
|
148
|
+
@config.logger.error e.backtrace
|
149
|
+
end
|
150
|
+
Thread.current['finished'] = true
|
151
|
+
@threads.synchronize do
|
152
|
+
@status_queue << success
|
153
|
+
end
|
154
|
+
@config.logger.info "Worker finished syncing key: #{opts[:key]}"
|
155
|
+
end
|
156
|
+
else
|
157
|
+
@config.logger.info "Queued work empty: #{opts}"
|
158
|
+
end
|
159
|
+
end
|
112
160
|
end
|
113
161
|
end
|
114
162
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: docker-registry-sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Oldfield
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-09-
|
11
|
+
date: 2015-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|