docker-registry-sync 0.2.11 → 0.2.12

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
- NzRjNGI0YjY1ZTYzNDY5ZTljZmE4NGY5MjI3MmI4M2VhMWZiYmU4YQ==
4
+ MzRkMjE5NGZlODE5MTMxM2ZmMWFmNzBiMGNiZDE3OWVlNmZmMDQ4Mw==
5
5
  data.tar.gz: !binary |-
6
- Nzk4MjJmZmM0OWIzZjY4MTAxZmI3OTljNGJiYmVhMDc2ODcxMjM3Yg==
6
+ MDI5MDVmM2Q4MzM5Y2Q2M2ZlNDQyYzU1Y2M5OWMyMGFhNDEyNzcyNg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZmJmMWQ1YTUxZTkyYTJlNzRjODVmMzk4MTYyZjc2YjJkNmI5ZTc1ODFiOTdl
10
- MjI5ODNhMjdlNThjYzhkYTIzZDZmNThmYzM5NzVlZTk1MDg1ODg2NmEwOTIz
11
- YmJmMmI5OTVhMDExZDBlYzg1ZGYyMGU4NWM0N2I2MmEyMjkyMzA=
9
+ OWRmZTdhNTNlMmY1ZTIzNmIyOGIzNGJhOTE5ZDAwMzI3YTlhYWU2ZmMyMzU5
10
+ NDhlMzk5Zjc1MGFlM2VmNTdhMzk0NjEzYWQ0MWQwNGY4MTY4MjkyYzEyMDdk
11
+ ZGI4OGU5NzBhZjVlNzI0NzVmN2EzMWIzNjZmZjViMWU3ZTliZGI=
12
12
  data.tar.gz: !binary |-
13
- M2ZmOGI3ZmY4NmRjYjFjMjNlZjViZWI1NGI4ODZkZDUwODcyNjFmZjkwYWQx
14
- YzQzNmJiYjBiMjM4ODI2NzEyMzczZjI5ZjE2OWFmY2RiOTljOTM5MjFhYzI0
15
- OTJjZjhlZjgwZTIyMTM3YzcwNjkxYjQ4MmZhOTBiZDk5YTM5MGY=
13
+ MGNmMmExZjBiODk1MzI0ZWY1NjUxZGNkYzZiOGQyMGUwZTY1YjA2ZjNjYmNk
14
+ MTBmYzhmOTAzYjU5Y2E5ZWQ4NTM0NWU0ZmIyOTNhZGNkYmI4OWRjZTkyOGFj
15
+ MGVjYzM5MDk1OTIyYzdjNGM4ZDU2MDI5ZjFhZjYwZDQxMDE5ODU=
data/.gitignore CHANGED
@@ -3,6 +3,7 @@
3
3
  bin/*
4
4
  !bin/docker-registry-sync
5
5
  .bundle/
6
+ Gemfile.lock
6
7
  coverage/
7
8
  install.sh
8
9
  test.rb
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -11,7 +11,9 @@ module Docker
11
11
  include Docker::Registry::Sync
12
12
 
13
13
  class << self
14
- def configure(source_bucket, target_buckets, sqs_queue, use_sse, source_uses_sse, pool)
14
+ attr_accessor :config, :producer_finished, :work_queue, :status_queue, :threads
15
+
16
+ def configure(source_bucket, target_buckets, sqs_queue, use_sse, source_uses_sse, pool, log_level = :debug)
15
17
  unless source_bucket.nil?
16
18
  source_region, source_bucket = source_bucket.split(':')
17
19
  else
@@ -41,6 +43,7 @@ module Docker
41
43
  config.sqs_region = sqs_region
42
44
  config.pool_size = pool
43
45
  config.sqs_url = "https://#{sqs_uri}"
46
+ config.log_level = log_level
44
47
  end
45
48
  @config = Docker::Registry::Sync.config
46
49
  end
@@ -66,7 +69,7 @@ module Docker
66
69
  end
67
70
  end
68
71
 
69
- def start_workers
72
+ def configure_workers
70
73
  @threads = Array.new(@config.pool_size)
71
74
  @work_queue = Queue.new
72
75
  @status_queue = Queue.new
@@ -75,6 +78,9 @@ module Docker
75
78
  @threads_available = @threads.new_cond
76
79
 
77
80
  @producer_finished = false
81
+ end
82
+
83
+ def start_workers
78
84
  @consumer_thread = Thread.new do
79
85
  sync_key_consumer
80
86
  end
@@ -85,6 +91,7 @@ module Docker
85
91
  @producer_finished = true
86
92
  end
87
93
  @consumer_thread.join
94
+ @consumer_thread = nil
88
95
  @threads.each { |t| t.join unless t.nil? }
89
96
  @config.logger.info "Processing job results..."
90
97
 
@@ -103,6 +110,7 @@ module Docker
103
110
 
104
111
  def sync(image, tag)
105
112
  configure_signal_handlers
113
+ configure_workers
106
114
  start_workers
107
115
  success = false
108
116
  @config.target_buckets.each do |region, bucket, sse|
@@ -153,6 +161,7 @@ module Docker
153
161
  @config.logger.info "Image sync data: #{data}"
154
162
 
155
163
  if image_exists?(data['image'], data['target']['bucket'], data['target']['region'])
164
+ configure_workers
156
165
  start_workers
157
166
  @config.logger.info("Syncing tag: #{data['image']}:#{data['tag']} to #{data['target']['region']}:#{data['target']['bucket']}")
158
167
  success = sync_tag(data['image'], data['tag'], data['target']['bucket'], data['target']['region'], data['target']['sse'], data['source']['bucket'], data['source']['region'])
@@ -165,6 +174,7 @@ module Docker
165
174
  @config.logger.info("Falied to sync tag, leaving on queue: #{data['image']}:#{data['tag']} to #{data['target']['region']}:#{data['target']['bucket']}")
166
175
  end
167
176
  else
177
+ configure_workers
168
178
  start_workers
169
179
  success = sync_repo(data['image'], data['target']['bucket'], data['target']['region'], data['target']['sse'], data['source']['bucket'], data['source']['region'])
170
180
  success &&= finalize_workers
@@ -179,12 +189,13 @@ module Docker
179
189
  end
180
190
  ec = 0
181
191
  sleep @config.empty_queue_sleep_time unless @terminated
182
- rescue => e
192
+ rescue StandardError => e
183
193
  @config.logger.error "An unknown error occurred while monitoring queue: #{e}"
184
194
  @config.logger.error e.traceback
185
195
  @config.logger.error 'Exiting...'
186
196
  @terminated = true
187
197
  ec = 1
198
+ finalize_workers # make sure there are no hangers-on
188
199
  end until @terminated
189
200
  ec
190
201
  end
@@ -34,7 +34,7 @@ module Docker
34
34
 
35
35
  img_id = s3_source.get_object(bucket: source_bucket, key: "registry/repositories/#{image}/tag_#{tag}").body.read
36
36
  sync_image(img_id, bucket, region, sse, source_bucket, source_region)
37
- rescue => e
37
+ rescue StandardError => e
38
38
  @config.logger.error "An unexpected error occoured while syncing tag #{image}:#{tag}: #{e}"
39
39
  @config.logger.error e.backtrace
40
40
  false
@@ -57,7 +57,7 @@ module Docker
57
57
  JSON.load(img_index_resp.body.read).each do |image|
58
58
  sync_image(image['id'], bucket, region, sse, source_bucket, source_region)
59
59
  end
60
- rescue => e
60
+ rescue StandardError => e
61
61
  @config.logger.error "An unexpected error occoured while syncing repo #{repo}: #{e}"
62
62
  @config.logger.error e.backtrace
63
63
  false
@@ -114,9 +114,7 @@ module Docker
114
114
  if @config.source_sse
115
115
  opts[:copy_source_sse_customer_algorithm] = 'AES256'
116
116
  end
117
- @threads.synchronize do
118
- @work_queue << opts
119
- end
117
+ @work_queue << opts
120
118
  sleep 0.1
121
119
  end
122
120
  end
@@ -134,9 +132,7 @@ module Docker
134
132
  t_index = @threads.rindex { |t| t.nil? || t.status == false || t['finished'].nil? == false }
135
133
 
136
134
  begin
137
- opts = @threads.synchronize do
138
- @work_queue.pop(true)
139
- end
135
+ opts = @work_queue.pop(true)
140
136
  rescue ThreadError
141
137
  @config.logger.info "No work found on the queue, sleeping..."
142
138
  sleep 1
@@ -152,14 +148,12 @@ module Docker
152
148
  target_client.copy_object(opts)
153
149
  success = true
154
150
  @config.logger.info "Worker finished syncing key: #{opts[:key]}"
155
- rescue => e
151
+ rescue StandardError => e
156
152
  @config.logger.error "An unknown error occoured while copying object in s3: #{e}"
157
153
  @config.logger.error e.backtrace
158
154
  ensure
159
155
  Thread.current['finished'] = true
160
- @threads.synchronize do
161
- @status_queue << success
162
- end
156
+ @status_queue << success
163
157
  end
164
158
  end
165
159
  else
@@ -1,7 +1,7 @@
1
1
  module Docker
2
2
  module Registry
3
3
  module Sync
4
- VERSION = '0.2.11'
4
+ VERSION = '0.2.12'
5
5
  end
6
6
  end
7
7
  end
data/spec/s3_spec.rb ADDED
@@ -0,0 +1,148 @@
1
+ require 'json'
2
+ require 'spec_helper'
3
+ require 'docker/registry/sync'
4
+ require 'aws-sdk'
5
+
6
+ def stub_s3_response(action, resp)
7
+ Aws.config[:s3] = { stub_responses: {} }
8
+ Aws.config[:s3][:stub_responses][action] = resp
9
+ end
10
+
11
+ describe 'Docker::Register::Sync::CMD' '#sync_tag' do
12
+ before do
13
+ Docker::Registry::Sync::CMD.configure(
14
+ 'us-west-2:test-source-bucket',
15
+ 'us-west-2:test-target-bucket',
16
+ 'us-west-2:test-sqs-queue',
17
+ false,
18
+ false,
19
+ 2,
20
+ :off
21
+ )
22
+ Docker::Registry::Sync::CMD.configure_workers
23
+ end
24
+
25
+ context 'syncing tags' do
26
+ it 'successfully syncs' do
27
+ img_id = 'sync-successful'
28
+ stub_s3_response(:get_object, { body: img_id })
29
+ allow(Docker::Registry::Sync::CMD).to receive(:sync_image).with(
30
+ img_id,
31
+ 'test-target-bucket',
32
+ 'us-west-2',
33
+ false,
34
+ 'test-source-bucket',
35
+ 'us-west-2'
36
+ )
37
+ allow(Docker::Registry::Sync::CMD).to receive(:sync_keys)
38
+ res = Docker::Registry::Sync::CMD.sync_tag('sync-image', 'sync-tag', 'test-target-bucket', 'us-west-2', false)
39
+ expect(res).to be true
40
+ end
41
+
42
+ it 'S3 API failure' do
43
+ img_id = 'sync-unsuccessful'
44
+ stub_s3_response(:get_object, RuntimeError.new('api failure'))
45
+ allow(Docker::Registry::Sync::CMD).to receive(:sync_keys)
46
+ res = Docker::Registry::Sync::CMD.sync_tag('sync-image', 'sync-tag', 'test-target-bucket', 'us-west-2', false)
47
+ expect(res).to be false
48
+ end
49
+ end
50
+ end
51
+
52
+ describe 'Docker::Register::Sync::CMD' '#sync_repo' do
53
+ before do
54
+ Docker::Registry::Sync::CMD.configure(
55
+ 'us-west-2:test-source-bucket',
56
+ 'us-west-2:test-target-bucket',
57
+ 'us-west-2:test-sqs-queue',
58
+ false,
59
+ false,
60
+ 2,
61
+ :off
62
+ )
63
+ Docker::Registry::Sync::CMD.configure_workers
64
+ end
65
+
66
+ context 'syncing repositories' do
67
+ it 'successfully syncs' do
68
+ images = [{'id' => 'image-one'}, {'id' => 'image-two'}]
69
+ allow(Docker::Registry::Sync::CMD).to receive(:sync_prefix)
70
+ images.each do |i|
71
+ allow(Docker::Registry::Sync::CMD).to receive(:sync_image).with(i['id'],'test-target-bucket', 'us-west-2', false, 'test-source-bucket', 'us-west-2')
72
+ end
73
+
74
+ stub_s3_response(:get_object, { body: JSON.dump(images)})
75
+ res = Docker::Registry::Sync::CMD.sync_repo('sync-repo', 'test-target-bucket', 'us-west-2', false)
76
+ expect(res).to be true
77
+ end
78
+
79
+ it 'S3 API failure' do
80
+ images = [{'id' => 'image-one'}, {'id' => 'image-two'}]
81
+ allow(Docker::Registry::Sync::CMD).to receive(:sync_prefix)
82
+ images.each do |i|
83
+ allow(Docker::Registry::Sync::CMD).to receive(:sync_image).with(i['id'],'test-target-bucket', 'us-west-2', false, 'test-source-bucket', 'us-west-2')
84
+ end
85
+
86
+ stub_s3_response(:get_object, RuntimeError.new('api failure'))
87
+ res = Docker::Registry::Sync::CMD.sync_repo('sync-repo', 'test-target-bucket', 'us-west-2', false)
88
+ expect(res).to be false
89
+ end
90
+ end
91
+ end
92
+
93
+ describe 'Docker::Register::Sync::CMD' '#sync_key_consumer' do
94
+ before do
95
+ Docker::Registry::Sync::CMD.configure(
96
+ 'us-west-2:test-source-bucket',
97
+ 'us-west-2:test-target-bucket',
98
+ 'us-west-2:test-sqs-queue',
99
+ false,
100
+ false,
101
+ 1,
102
+ :off
103
+ )
104
+ Docker::Registry::Sync::CMD.configure_workers
105
+ end
106
+
107
+ context 'runs' do
108
+ it 'successfully exits with no work and producer is finished' do
109
+ t = Thread.new do
110
+ Docker::Registry::Sync::CMD.sync_key_consumer
111
+ end
112
+ Docker::Registry::Sync::CMD.producer_finished = true
113
+ t.join
114
+ expect(Docker::Registry::Sync::CMD.work_queue.length).to be 0
115
+ expect(t.status).to be false
116
+ end
117
+
118
+ it 'successfully exits when a child encounters an exception' do
119
+ Docker::Registry::Sync::CMD.work_queue << {
120
+ region: 'us-west-2',
121
+ key: 'exception-raiser',
122
+ bucket: 'exception-raiser',
123
+ copy_source: 'exception-raiser',
124
+ }
125
+ Docker::Registry::Sync::CMD.work_queue << {
126
+ region: 'us-west-2',
127
+ key: 'exception-raiser',
128
+ bucket: 'exception-raiser',
129
+ copy_source: 'exception-raiser',
130
+ }
131
+ Docker::Registry::Sync::CMD.producer_finished = true
132
+ stub_s3_response(:copy_object, RuntimeError.new('api failure'))
133
+ t = Thread.new do
134
+ Docker::Registry::Sync::CMD.sync_key_consumer
135
+ end
136
+
137
+ t.join
138
+ Docker::Registry::Sync::CMD.threads.each { |t| t.join }
139
+ expect(t.status).to be false
140
+ expect(Docker::Registry::Sync::CMD.threads[0].status).to be false
141
+
142
+ expect(Docker::Registry::Sync::CMD.work_queue.length).to be 0
143
+ expect(Docker::Registry::Sync::CMD.status_queue.length).to be 2
144
+ expect(Docker::Registry::Sync::CMD.status_queue.pop).to be false
145
+ expect(Docker::Registry::Sync::CMD.status_queue.pop).to be false
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,66 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+ require 'webmock/rspec'
20
+ require 'rack'
21
+ require 'simplecov'
22
+ require 'simplecov-console'
23
+
24
+ WebMock.disable_net_connect!(allow_localhost: false)
25
+ DIR = File.expand_path(File.dirname(__FILE__))
26
+
27
+ class S3ApiRack
28
+ def call(env)
29
+ code, ret = 500, 'The tests have changed, fix the rack'
30
+ [code, { 'Content-Type' => 'application/json' }, [ret]]
31
+ end
32
+ end
33
+
34
+ RSpec.configure do |config|
35
+ config.expect_with :rspec do |expectations|
36
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
37
+ end
38
+ config.mock_with :rspec do |mocks|
39
+ mocks.verify_partial_doubles = true
40
+ end
41
+ #
42
+ config.before(:each) do
43
+ # Leaving in place to prevent accidental api calls
44
+ stub_request(:any, /127.0.0.1\//).
45
+ to_rack(S3ApiRack.new)
46
+ end
47
+
48
+ end
49
+
50
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
51
+ SimpleCov::Formatter::HTMLFormatter,
52
+ SimpleCov::Formatter::Console
53
+ ]
54
+ SimpleCov.minimum_coverage(80)
55
+ SimpleCov.start
56
+
57
+ def capture_stdout(&block)
58
+ original_stdout = $stdout
59
+ $stdout = fake = StringIO.new
60
+ begin
61
+ yield
62
+ ensure
63
+ $stdout = original_stdout
64
+ end
65
+ #fake.string
66
+ 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.2.11
4
+ version: 0.2.12
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-10-22 00:00:00.000000000 Z
11
+ date: 2015-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -132,6 +132,7 @@ extensions: []
132
132
  extra_rdoc_files: []
133
133
  files:
134
134
  - .gitignore
135
+ - .rspec
135
136
  - Gemfile
136
137
  - README.md
137
138
  - bin/docker-registry-sync
@@ -142,6 +143,8 @@ files:
142
143
  - lib/docker/registry/sync/s3.rb
143
144
  - lib/docker/registry/sync/sqs.rb
144
145
  - lib/docker/registry/sync/version.rb
146
+ - spec/s3_spec.rb
147
+ - spec/spec_helper.rb
145
148
  homepage: http://github.com/socrata-platform/docker-registry-sync
146
149
  licenses:
147
150
  - Apache
@@ -167,4 +170,6 @@ signing_key:
167
170
  specification_version: 4
168
171
  summary: Sync data between S3 two distinct docker registries using S3 data storage
169
172
  and SNS as a messaging system.
170
- test_files: []
173
+ test_files:
174
+ - spec/s3_spec.rb
175
+ - spec/spec_helper.rb