docker-registry-sync 0.1.0
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 +15 -0
- data/.gitignore +8 -0
- data/Gemfile +3 -0
- data/README.md +0 -0
- data/bin/docker-registry-sync +104 -0
- data/docker-registry-sync.gemspec +29 -0
- data/lib/docker/registry/sync.rb +18 -0
- data/lib/docker/registry/sync/cmd.rb +118 -0
- data/lib/docker/registry/sync/configuration.rb +73 -0
- data/lib/docker/registry/sync/s3.rb +100 -0
- data/lib/docker/registry/sync/sqs.rb +48 -0
- data/lib/docker/registry/sync/version.rb +7 -0
- metadata +170 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MTUwYjg1N2FmMjBmMzYyZjQ2OTJiNDZiY2ZjMWI2ZTFiOGRiNGVjYg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MzhhYWY5OTkxMmNiMDczYTU3NjBhMjI3ZTg3MWVhZmMwZDYxNmFkMw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NjNlYjg4OWYwNWM4MDM3ZmJkNjY3M2M3ZDk3OGUzOTZjMWZmYzQzYjUzNDQ1
|
10
|
+
ZWNkYTk1MzMwMGYxM2IzOGQzOTEwODFhYWRmYjhlNGExMDNmZDliOTNhM2Zj
|
11
|
+
ZWU5Y2YzZGQ2MTlkMGVmZGM2MzYzNjgyOTdmY2E3OWE0OTQ3ZTA=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZmYxY2NjZTUyZjNjMGI0M2FmNDExZTU4MmM1NGM5MGZlMzczNTlmMmY0NjE3
|
14
|
+
YzM1MzA1Y2M5Y2ExNTJlMjk3YTg3YWVmZmEzOTU2MmE1ZjAxNTgzYmQ1ZmZh
|
15
|
+
NjYyNDk2N2IxMzNhMWRiYjk5MjNkMGM2ZDkxYWZiODUzYTcwZDk=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
File without changes
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'docker/registry/sync'
|
4
|
+
|
5
|
+
include Docker::Registry::Sync
|
6
|
+
|
7
|
+
def verify_opts(opts, command)
|
8
|
+
source_defined = !opts[:source_bucket].nil?
|
9
|
+
dest_defined = !opts[:target_buckets].nil?
|
10
|
+
queue_defined = !opts[:sqs_queue].nil?
|
11
|
+
if command == 'sync'
|
12
|
+
source_defined && dest_defined
|
13
|
+
elsif command == 'queue-sync'
|
14
|
+
source_defined && dest_defined && queue_defined
|
15
|
+
elsif command == 'run-sync'
|
16
|
+
queue_defined
|
17
|
+
else
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
options = {}
|
23
|
+
opt_parser = OptionParser.new do |opts|
|
24
|
+
opts.banner = <<EOC
|
25
|
+
docker-registry-sync [options] sync <image>:<tag>
|
26
|
+
docker-registry-sync [options] queue-sync <image>:<tag>
|
27
|
+
docker-registry-sync [options] run-sync
|
28
|
+
|
29
|
+
Available commands:
|
30
|
+
sync -- Sync docker image from one source registry S3 bucket to one or more S3 buckets.
|
31
|
+
|
32
|
+
Options:
|
33
|
+
EOC
|
34
|
+
options[:source_bucket] = nil
|
35
|
+
opts.on('-s SOUCE', '--source-bucket SOURCE', 'Primary docker registry S3 bucket. Pass as <region>:<bucket> [required]') do |b|
|
36
|
+
options[:source_bucket] = b
|
37
|
+
end
|
38
|
+
|
39
|
+
options[:target_buckets] = nil
|
40
|
+
opts.on('-t TARGETS', '--target-buckets TARGETS', 'S3 buckets to sync to, comma separated. Pass as <region>:<bucket>[,<region>:<bucket>] [required]') do |t|
|
41
|
+
options[:target_buckets] = t
|
42
|
+
end
|
43
|
+
|
44
|
+
options[:sqs_queue] = nil
|
45
|
+
opts.on('-q SQS_QUEUE', '--queue SQS_QUEUE', 'SQS queue url used to enqueue sync job. Pass as <region>:<uri> (do not include schema) [required if using queue-sync or run-sync]') do |b|
|
46
|
+
options[:sqs_queue] = b
|
47
|
+
end
|
48
|
+
|
49
|
+
options[:unset_proxy] = false
|
50
|
+
opts.on(nil, '--unset-proxy', "Use if 'http_proxy' is set in your environment, but you don't want to use it...") do |_u|
|
51
|
+
options[:unset_proxy] = true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
opt_parser.version = VERSION
|
56
|
+
opt_parser.parse!
|
57
|
+
|
58
|
+
cmd = ARGV[0]
|
59
|
+
if cmd.nil?
|
60
|
+
puts "Command must be supplied!\n\n"
|
61
|
+
puts opt_parser
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
|
65
|
+
image = ARGV[1]
|
66
|
+
if image.nil? && !['queue-sync', 'run-sync'].include?(cmd)
|
67
|
+
puts "Image and tag to sync must be supplied!\n\n"
|
68
|
+
puts opt_parser
|
69
|
+
exit 1
|
70
|
+
end
|
71
|
+
|
72
|
+
ENV['http_proxy'] = nil if options[:unset_proxy]
|
73
|
+
|
74
|
+
unless verify_opts(options, cmd)
|
75
|
+
puts "Both '--source-bucket' and '--target-buckets' must be defined for 'sync' and 'queue-sync' commands\n"
|
76
|
+
puts "'--queue' must be defined for 'queue-sync' and 'run-sync' commands\n\n"
|
77
|
+
puts opt_parser
|
78
|
+
exit 1
|
79
|
+
end
|
80
|
+
|
81
|
+
CMD.configure(
|
82
|
+
options[:source_bucket],
|
83
|
+
options[:target_buckets],
|
84
|
+
options[:sqs_queue]
|
85
|
+
)
|
86
|
+
|
87
|
+
ec = 1
|
88
|
+
|
89
|
+
case cmd
|
90
|
+
when 'sync'
|
91
|
+
image, tag = image.split(':')
|
92
|
+
ec = CMD.sync(image, tag)
|
93
|
+
when 'queue-sync'
|
94
|
+
image, tag = image.split(':')
|
95
|
+
ec = CMD.queue_sync(image, tag)
|
96
|
+
when 'run-sync'
|
97
|
+
ec = CMD.run_sync
|
98
|
+
else
|
99
|
+
puts "Unknown command: #{cmd}"
|
100
|
+
puts opt_parser
|
101
|
+
ec = 1
|
102
|
+
end
|
103
|
+
|
104
|
+
exit(ec)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'docker/registry/sync'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'docker-registry-sync'
|
8
|
+
spec.version = Docker::Registry::Sync::VERSION
|
9
|
+
spec.authors = ['Brian Oldfield']
|
10
|
+
spec.email = ['brian.oldfield@socrata.com']
|
11
|
+
spec.summary = 'Sync data between S3 two distinct docker registries using S3 data storage and SNS as a messaging system.'
|
12
|
+
spec.description = 'Sync data between S3 two distinct docker registries using S3 data storage and SNS as a messaging system.'
|
13
|
+
spec.homepage = 'http://github.com/socrata-platform/docker-registry-sync'
|
14
|
+
spec.license = 'Apache'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/docker-registry-sync}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
spec.add_development_dependency 'rspec', '~> 3.3'
|
24
|
+
spec.add_development_dependency 'simplecov', '~> 0.10'
|
25
|
+
spec.add_development_dependency 'simplecov-console', '~> 0.2'
|
26
|
+
spec.add_development_dependency 'webmock', '~> 1.21'
|
27
|
+
spec.add_development_dependency 'rack', '~> 1.6'
|
28
|
+
spec.add_dependency 'aws-sdk', '~> 2.0'
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'docker/registry/sync/cmd'
|
2
|
+
require 'docker/registry/sync/configuration'
|
3
|
+
require 'docker/registry/sync/version'
|
4
|
+
|
5
|
+
module Docker
|
6
|
+
module Registry
|
7
|
+
module Sync
|
8
|
+
class << self
|
9
|
+
attr_accessor :config
|
10
|
+
|
11
|
+
def configure
|
12
|
+
self.config ||= Docker::Registry::Sync::Configuration.new
|
13
|
+
yield self.config
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'docker/registry/sync/s3'
|
3
|
+
require 'docker/registry/sync/sqs'
|
4
|
+
|
5
|
+
module Docker
|
6
|
+
module Registry
|
7
|
+
module Sync
|
8
|
+
module CMD
|
9
|
+
include Docker::Registry::Sync
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def configure(source_bucket, target_buckets, sqs_queue)
|
13
|
+
source_region, source_bucket = source_bucket.split(':')
|
14
|
+
target_buckets = target_buckets.split(',').collect { |bucket| bucket.split(':') }
|
15
|
+
sqs_region, sqs_uri = sqs_queue.split(':')
|
16
|
+
Docker::Registry::Sync.configure do |config|
|
17
|
+
config.source_bucket = source_bucket
|
18
|
+
config.source_region = source_region
|
19
|
+
config.target_buckets = target_buckets
|
20
|
+
config.sqs_region = sqs_region
|
21
|
+
config.sqs_url = "https://#{sqs_uri}"
|
22
|
+
end
|
23
|
+
@config = Docker::Registry::Sync.config
|
24
|
+
end
|
25
|
+
|
26
|
+
def configure_signal_handlers
|
27
|
+
@terminated = false
|
28
|
+
|
29
|
+
Signal.trap('INT') do
|
30
|
+
@config.logger.error 'Received INT signal...'
|
31
|
+
@terminated = true
|
32
|
+
end
|
33
|
+
Signal.trap('TERM') do
|
34
|
+
@config.logger.error 'Received TERM signal...'
|
35
|
+
@terminated = true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def sync(image, tag)
|
40
|
+
success = false
|
41
|
+
@config.target_buckets.each do |region, bucket|
|
42
|
+
if image_exists?(image, bucket, region)
|
43
|
+
success = sync_tag(image, tag, bucket, region)
|
44
|
+
else
|
45
|
+
success = sync_image(image, bucket, region)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
success ? 0 : 1
|
49
|
+
end
|
50
|
+
|
51
|
+
def queue_sync(image, tag)
|
52
|
+
msgs = @config.target_buckets.map do |region, bucket|
|
53
|
+
JSON.dump(retries: 0,
|
54
|
+
image: image,
|
55
|
+
tag: tag,
|
56
|
+
source: {
|
57
|
+
bucket: @config.source_bucket,
|
58
|
+
region: @config.source_region
|
59
|
+
},
|
60
|
+
target: {
|
61
|
+
bucket: bucket,
|
62
|
+
region: region
|
63
|
+
})
|
64
|
+
end
|
65
|
+
send_message_batch(msgs) ? 0 : 1
|
66
|
+
end
|
67
|
+
|
68
|
+
def run_sync
|
69
|
+
ec = 1
|
70
|
+
configure_signal_handlers
|
71
|
+
begin
|
72
|
+
@config.logger.info 'Polling queue for images to sync...'
|
73
|
+
sqs = Aws::SQS::Client.new(region: @config.sqs_region)
|
74
|
+
resp = sqs.receive_message(
|
75
|
+
queue_url: @config.sqs_url,
|
76
|
+
max_number_of_messages: 1,
|
77
|
+
visibility_timeout: 900, # Give ourselves 15min to sync the image
|
78
|
+
wait_time_seconds: 10, # Wait a maximum of 10s for a new message
|
79
|
+
)
|
80
|
+
@config.logger.info "SQS returned #{resp.messages.length} new images to sync..."
|
81
|
+
if resp.messages.length == 1
|
82
|
+
message = resp.messages[0]
|
83
|
+
data = JSON.load(message.body)
|
84
|
+
@config.logger.info "Image sync data: #{data}"
|
85
|
+
|
86
|
+
if image_exists?(data['image'], data['target']['bucket'], data['target']['region'])
|
87
|
+
@config.logger.info("Syncing tag: #{data['image']}:#{data['tag']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
88
|
+
if sync_tag(data['image'], data['tag'], data['target']['bucket'], data['target']['region'], data['source']['bucket'], data['source']['region'])
|
89
|
+
@config.logger.info("Finished syncing tag: #{data['image']}:#{data['tag']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
90
|
+
finalize_message(message.receipt_handle)
|
91
|
+
else
|
92
|
+
@config.logger.info("Falied to sync tag, leaving on queue: #{data['image']}:#{data['tag']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
93
|
+
end
|
94
|
+
else
|
95
|
+
@config.logger.info("Syncing image: #{data['image']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
96
|
+
if sync_image(data['image'], data['target']['bucket'], data['target']['region'], data['source']['bucket'], data['source']['region'])
|
97
|
+
@config.logger.info("Finished syncing image: #{data['image']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
98
|
+
finalize_message(message.receipt_handle)
|
99
|
+
else
|
100
|
+
@config.logger.error("Failed to sync image, leaving on queue: #{data['image']} to #{data['target']['region']}:#{data['target']['bucket']}")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
ec = 0
|
105
|
+
sleep @config.empty_queue_sleep_time unless @terminated
|
106
|
+
rescue Exception => e
|
107
|
+
@config.logger.error "An unknown error occurred while monitoring queue: #{e}"
|
108
|
+
@config.logger.error 'Exiting...'
|
109
|
+
@terminated = true
|
110
|
+
ec = 1
|
111
|
+
end until @terminated
|
112
|
+
ec
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Docker
|
2
|
+
module Registry
|
3
|
+
module Sync
|
4
|
+
module STDLogLvl
|
5
|
+
class << self
|
6
|
+
def debug
|
7
|
+
1
|
8
|
+
end
|
9
|
+
|
10
|
+
def info
|
11
|
+
2
|
12
|
+
end
|
13
|
+
|
14
|
+
def error
|
15
|
+
3
|
16
|
+
end
|
17
|
+
|
18
|
+
def off
|
19
|
+
4
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module STDLogger
|
25
|
+
class << self
|
26
|
+
def do_log?(requested_lvl, curr_lvl)
|
27
|
+
curr_lvl = Docker::Registry::Sync::STDLogLvl.send(curr_lvl.to_sym)
|
28
|
+
requested_lvl = Docker::Registry::Sync::STDLogLvl.send(requested_lvl.to_sym)
|
29
|
+
requested_lvl >= curr_lvl
|
30
|
+
end
|
31
|
+
|
32
|
+
def debug(msg)
|
33
|
+
if do_log?(:debug, Docker::Registry::Sync.config.log_level)
|
34
|
+
STDOUT.puts "[DEBUG] #{msg}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def info(msg)
|
39
|
+
if do_log?(:info, Docker::Registry::Sync.config.log_level)
|
40
|
+
STDOUT.puts "[INFO] #{msg}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def error(msg)
|
45
|
+
if do_log?(:error, Docker::Registry::Sync.config.log_level)
|
46
|
+
STDERR.puts "[ERROR] #{msg}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Configuration
|
53
|
+
attr_accessor :source_bucket, :source_region, :target_buckets, :sqs_region, :sqs_url, :empty_queue_sleep_time
|
54
|
+
attr_accessor :log_level, :logger
|
55
|
+
|
56
|
+
def initialize
|
57
|
+
@source_bucket = nil
|
58
|
+
@source_region = nil
|
59
|
+
|
60
|
+
@target_buckets = nil
|
61
|
+
|
62
|
+
@sqs_region = nil
|
63
|
+
@sqs_url = nil
|
64
|
+
|
65
|
+
@empty_queue_sleep_time = 5
|
66
|
+
|
67
|
+
@log_level = :debug
|
68
|
+
@logger = Docker::Registry::Sync::STDLogger
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
module Docker
|
4
|
+
module Registry
|
5
|
+
module Sync
|
6
|
+
module CMD
|
7
|
+
include Docker::Registry::Sync
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def image_exists?(image, bucket, region)
|
11
|
+
s3 = Aws::S3::Client.new(region: region)
|
12
|
+
begin
|
13
|
+
s3.head_object(bucket: bucket, key: "registry/repositories/#{image}/_index_images")
|
14
|
+
rescue Aws::S3::Errors::NotFound
|
15
|
+
false
|
16
|
+
else
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def sync_prefix(source_client, target_client, target_bucket, prefix, source_bucket = nil)
|
22
|
+
source_bucket ||= @config.source_bucket
|
23
|
+
keys = []
|
24
|
+
img_resp = source_client.list_objects(bucket: source_bucket, prefix: prefix)
|
25
|
+
|
26
|
+
loop do
|
27
|
+
img_resp.contents.each do |item|
|
28
|
+
keys << item.key
|
29
|
+
end
|
30
|
+
if img_resp.last_page?
|
31
|
+
break
|
32
|
+
else
|
33
|
+
img_resp.next_page
|
34
|
+
end
|
35
|
+
end
|
36
|
+
sync_keys(target_client, target_bucket, keys)
|
37
|
+
end
|
38
|
+
|
39
|
+
def sync_keys(target_client, target_bucket, keys, source_bucket = nil)
|
40
|
+
source_bucket ||= @config.source_bucket
|
41
|
+
keys.each do |key|
|
42
|
+
@config.logger.info "Syncing key #{source_bucket}/#{key} to bucket #{target_bucket}"
|
43
|
+
target_client.copy_object(acl: 'bucket-owner-full-control',
|
44
|
+
bucket: target_bucket,
|
45
|
+
key: key,
|
46
|
+
copy_source: "#{source_bucket}/#{key}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def sync_tag(image, tag, bucket, region, source_bucket = nil, source_region = nil)
|
51
|
+
source_region ||= @config.source_region
|
52
|
+
source_bucket ||= @config.source_bucket
|
53
|
+
|
54
|
+
s3_source = Aws::S3::Client.new(region: source_region)
|
55
|
+
s3_target = Aws::S3::Client.new(region: region)
|
56
|
+
|
57
|
+
begin
|
58
|
+
keys = ["tag#{tag}_json", "tag_#{tag}", '_index_images'].map do |key|
|
59
|
+
"registry/repositories/#{image}/#{key}"
|
60
|
+
end
|
61
|
+
sync_keys(s3_target, bucket, keys)
|
62
|
+
|
63
|
+
img_id_resp = s3_source.get_object(bucket: source_bucket, key: "registry/repositories/#{image}/tag_#{tag}")
|
64
|
+
img_prefix = "registry/images/#{img_id_resp.body.read}/"
|
65
|
+
sync_prefix(s3_source, s3_target, bucket, img_prefix)
|
66
|
+
rescue Exception => e
|
67
|
+
@config.logger.error "An unexpected error occoured while syncing tag #{image}:#{tag}: #{e}"
|
68
|
+
false
|
69
|
+
else
|
70
|
+
true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def sync_image(image, bucket, region, source_bucket = nil, source_region = nil)
|
75
|
+
source_region ||= @config.source_region
|
76
|
+
source_bucket ||= @config.source_bucket
|
77
|
+
s3_source = Aws::S3::Client.new(region: source_region)
|
78
|
+
s3_target = Aws::S3::Client.new(region: region)
|
79
|
+
|
80
|
+
begin
|
81
|
+
rep_prefix = "registry/repositories/#{image}/"
|
82
|
+
sync_prefix(s3_source, s3_target, bucket, rep_prefix)
|
83
|
+
|
84
|
+
img_index_resp = s3_source.get_object(bucket: source_bucket, key: "registry/repositories/#{image}/_index_images")
|
85
|
+
JSON.load(img_index_resp.body.read).each do |image|
|
86
|
+
image_prefix = "registry/images/#{image['id']}/"
|
87
|
+
sync_prefix(s3_source, s3_target, bucket, image_prefix)
|
88
|
+
end
|
89
|
+
rescue Exception => e
|
90
|
+
@config.logger.error "An unexpected error occoured while syncing image #{image}: #{e}"
|
91
|
+
false
|
92
|
+
else
|
93
|
+
true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'aws-sdk'
|
3
|
+
|
4
|
+
module Docker
|
5
|
+
module Registry
|
6
|
+
module Sync
|
7
|
+
module CMD
|
8
|
+
include Docker::Registry::Sync
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def send_message_batch(messages, retries = 5)
|
12
|
+
if retries <= 0
|
13
|
+
success = false
|
14
|
+
messages.each do |msg|
|
15
|
+
@config.logger.Error "Failed to Enqueue message: #{msg}"
|
16
|
+
end
|
17
|
+
else
|
18
|
+
entries = messages.map do |msg|
|
19
|
+
@config.logger.info "Enqueuing message: #{msg}"
|
20
|
+
{ id: Digest::MD5.hexdigest(msg), message_body: msg }
|
21
|
+
end
|
22
|
+
sqs = Aws::SQS::Client.new(region: @config.sqs_region)
|
23
|
+
resp = sqs.send_message_batch(queue_url: @config.sqs_url,
|
24
|
+
entries: entries)
|
25
|
+
if resp.failed.length > 0
|
26
|
+
rerun = resp.failed.map do |failed|
|
27
|
+
@config.logger.warn "Failed to Enqueue message, re-enqueuing: #{msg}"
|
28
|
+
messages.select { |m| Digest::MD5.hexdigest(m) == failed.id }[0]
|
29
|
+
end
|
30
|
+
sleep 1 # short sleep before trying again...
|
31
|
+
success = send_message_batch(rerun, retries - 1)
|
32
|
+
else
|
33
|
+
success = true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
success
|
37
|
+
end
|
38
|
+
|
39
|
+
def finalize_message(receipt_handle)
|
40
|
+
sqs = Aws::SQS::Client.new(region: @config.sqs_region)
|
41
|
+
resp = sqs.delete_message(queue_url: @config.sqs_url,
|
42
|
+
receipt_handle: receipt_handle)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: docker-registry-sync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Oldfield
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.10'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov-console
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.2'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.2'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: webmock
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.21'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.21'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rack
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.6'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.6'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: aws-sdk
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '2.0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '2.0'
|
125
|
+
description: Sync data between S3 two distinct docker registries using S3 data storage
|
126
|
+
and SNS as a messaging system.
|
127
|
+
email:
|
128
|
+
- brian.oldfield@socrata.com
|
129
|
+
executables:
|
130
|
+
- docker-registry-sync
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- .gitignore
|
135
|
+
- Gemfile
|
136
|
+
- README.md
|
137
|
+
- bin/docker-registry-sync
|
138
|
+
- docker-registry-sync.gemspec
|
139
|
+
- lib/docker/registry/sync.rb
|
140
|
+
- lib/docker/registry/sync/cmd.rb
|
141
|
+
- lib/docker/registry/sync/configuration.rb
|
142
|
+
- lib/docker/registry/sync/s3.rb
|
143
|
+
- lib/docker/registry/sync/sqs.rb
|
144
|
+
- lib/docker/registry/sync/version.rb
|
145
|
+
homepage: http://github.com/socrata-platform/docker-registry-sync
|
146
|
+
licenses:
|
147
|
+
- Apache
|
148
|
+
metadata: {}
|
149
|
+
post_install_message:
|
150
|
+
rdoc_options: []
|
151
|
+
require_paths:
|
152
|
+
- lib
|
153
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ! '>='
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
requirements: []
|
164
|
+
rubyforge_project:
|
165
|
+
rubygems_version: 2.4.6
|
166
|
+
signing_key:
|
167
|
+
specification_version: 4
|
168
|
+
summary: Sync data between S3 two distinct docker registries using S3 data storage
|
169
|
+
and SNS as a messaging system.
|
170
|
+
test_files: []
|