docker-registry-sync 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
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
|