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
- MDJmMGE4YzQyMmNjMmE3ZmEzNzI1MTIyZDgyMTgyMDJhOWQ3OGMxOA==
4
+ MjJjMmUyZWRiNWY1NjYzMDI1MTZiMjAxZDZhMWY5MDgzZjNjOTFiOA==
5
5
  data.tar.gz: !binary |-
6
- YWVhNDY4ZTYwYTcwMmRlNWEwODU5ZDYyMzg2ZmMzNDg5ZDVhNmExMQ==
6
+ Nzc5MWQxNzhiZmUxOGU0NTRlNzEyYTU2MmEzODE5ODhiMTE3MDllMw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NzFjNDc1MzFiNjM2ZGQ2ZWRlMDE2OGQyZTcyNjFjMGU3ZjAyNTUyZjQzODQx
10
- YTUxYTQxNmU4NDhjNWFhNmM0NmQ3NDA1YjZjMGQxNDIxMDBkZTYwNDFlNzMw
11
- NTdmYzBiNzViYWU0NzdjODM1NzZkMzU1ZjE3MTc0YWVjMzAwNzQ=
9
+ Yzc0MTExY2M3MjkxYTFlNzQxY2ExYjhiMTg5YTI0ODI1MTY4ZDdkNDA4YmQ2
10
+ MTA3ODNkMzU1ZWExODcyZTViNDExMjgzZGQ4ZjdjMDk1ZDQxNWE1YmFmNzI4
11
+ YWI2ZWVkN2VmM2NlZTYyMWExMmVjZDY3OGJiZTExY2Y3NGRkZjk=
12
12
  data.tar.gz: !binary |-
13
- NGRlODNhMjljYTI1MGNiYTAwMWE5ZWQ2NDU3MGY1YTZkNTFjZjgzNTMwMjg5
14
- Yzc0YmY0MGVkYTNkYmI5MDI2NzVmZjE3MjhjNDNhNTk5YmQ4YzA2ODA5Yjdk
15
- MTIyYjZmNGZhYTI2YmMyNjhhNzczOTZlY2YxZWU2OTU1ZDIyYjU=
13
+ NzIyMzM5OWY2ZDRkYTA2MmZlMDE5Y2ViYTZkYTE1YjE3NWVlMmFhY2RjNzZk
14
+ YTM1YjJhNWQxNzQ2NDEwMjU5ZTY2NjhiZjJmOWMyNWJkYmVmZGQzYzQ5ODAw
15
+ MjJkMmY4ZjZkNmQyZGFkOGFjMTBmYzRkNWEwNWNlOGE2ZDQyODU=
@@ -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
- if sync_tag(data['image'], data['tag'], data['target']['bucket'], data['target']['region'], data['target']['sse'], data['source']['bucket'], data['source']['region'])
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 sync_repo(data['image'], data['target']['bucket'], data['target']['region'], data['target']['sse'], data['source']['bucket'], data['source']['region'])
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
- target_client.copy_object(opts)
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
@@ -1,7 +1,7 @@
1
1
  module Docker
2
2
  module Registry
3
3
  module Sync
4
- VERSION = '0.1.5'
4
+ VERSION = '0.2.0'
5
5
  end
6
6
  end
7
7
  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.1.5
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-04 00:00:00.000000000 Z
11
+ date: 2015-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler