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 +8 -8
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/lib/docker/registry/sync/cmd.rb +14 -3
- data/lib/docker/registry/sync/s3.rb +6 -12
- data/lib/docker/registry/sync/version.rb +1 -1
- data/spec/s3_spec.rb +148 -0
- data/spec/spec_helper.rb +66 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MzRkMjE5NGZlODE5MTMxM2ZmMWFmNzBiMGNiZDE3OWVlNmZmMDQ4Mw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MDI5MDVmM2Q4MzM5Y2Q2M2ZlNDQyYzU1Y2M5OWMyMGFhNDEyNzcyNg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OWRmZTdhNTNlMmY1ZTIzNmIyOGIzNGJhOTE5ZDAwMzI3YTlhYWU2ZmMyMzU5
|
10
|
+
NDhlMzk5Zjc1MGFlM2VmNTdhMzk0NjEzYWQ0MWQwNGY4MTY4MjkyYzEyMDdk
|
11
|
+
ZGI4OGU5NzBhZjVlNzI0NzVmN2EzMWIzNjZmZjViMWU3ZTliZGI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MGNmMmExZjBiODk1MzI0ZWY1NjUxZGNkYzZiOGQyMGUwZTY1YjA2ZjNjYmNk
|
14
|
+
MTBmYzhmOTAzYjU5Y2E5ZWQ4NTM0NWU0ZmIyOTNhZGNkYmI4OWRjZTkyOGFj
|
15
|
+
MGVjYzM5MDk1OTIyYzdjNGM4ZDU2MDI5ZjFhZjYwZDQxMDE5ODU=
|
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -11,7 +11,9 @@ module Docker
|
|
11
11
|
include Docker::Registry::Sync
|
12
12
|
|
13
13
|
class << self
|
14
|
-
|
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
|
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
|
-
@
|
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 = @
|
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
|
-
@
|
161
|
-
@status_queue << success
|
162
|
-
end
|
156
|
+
@status_queue << success
|
163
157
|
end
|
164
158
|
end
|
165
159
|
else
|
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
|
data/spec/spec_helper.rb
ADDED
@@ -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.
|
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-
|
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
|