docker-registry-sync 0.2.11 → 0.2.12

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
- 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