docker-sync 0.0.4
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 +7 -0
- data/Thorfile +7 -0
- data/bin/docker-sync +11 -0
- data/lib/config.rb +59 -0
- data/lib/docker_sync/sync_manager.rb +133 -0
- data/lib/docker_sync/sync_process.rb +133 -0
- data/lib/execution.rb +34 -0
- data/tasks/sync.thor +81 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6f672a525e3ac21a5fe659eb7d0a15d8ba617e14
|
4
|
+
data.tar.gz: cbacd747e7959c1319c1f897070bd1d6d3a7da18
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c9ac6925db1d1e9da1088d6f85c82a0a47e4183933da35873df1aa02d7b93ed868976705cd496b9b98afaf41f10f551c1b1ca7f1baa95397b87e8cfe2aeb59cb
|
7
|
+
data.tar.gz: 973e7208ff7e7f9d560ba96f3a93d40bd56f7a82f4f02207a865248cb6d9c11dbaf0785ef6ab677f2e9205198e1b96a61310ee5810a53d1730aa87b0e34e238c
|
data/Thorfile
ADDED
data/bin/docker-sync
ADDED
data/lib/config.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'Pathname'
|
3
|
+
# this has basically completely reused from Thor::runner.rb - thank you!
|
4
|
+
|
5
|
+
module DockerSyncConfig
|
6
|
+
def find_config
|
7
|
+
files = find_config_file
|
8
|
+
if files.length > 0
|
9
|
+
return files.pop
|
10
|
+
else
|
11
|
+
raise('No docker-sync.yml configuration found in your path ( traversing up ) Did you define it for your project?')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
# this has been ruthlessly stolen from Thor/runner.rb - please do not hunt me for that :)
|
15
|
+
def find_config_file(skip_lookup = false)
|
16
|
+
# Finds docker-sync.yml by traversing from your current directory down to the root
|
17
|
+
# directory of your system. If at any time we find a docker-sync.yml file, we stop.
|
18
|
+
#
|
19
|
+
#
|
20
|
+
# ==== Example
|
21
|
+
#
|
22
|
+
# If we start at /Users/wycats/dev/thor ...
|
23
|
+
#
|
24
|
+
# 1. /Users/wycats/dev/thor
|
25
|
+
# 2. /Users/wycats/dev
|
26
|
+
# 3. /Users/wycats <-- we find a docker-sync.yml here, so we stop
|
27
|
+
#
|
28
|
+
# Suppose we start at c:\Documents and Settings\james\dev\docker-sync ...
|
29
|
+
#
|
30
|
+
# 1. c:\Documents and Settings\james\dev\docker-sync.yml
|
31
|
+
# 2. c:\Documents and Settings\james\dev
|
32
|
+
# 3. c:\Documents and Settings\james
|
33
|
+
# 4. c:\Documents and Settings
|
34
|
+
# 5. c:\ <-- no docker-sync.yml found!
|
35
|
+
#
|
36
|
+
docker_sync_files = []
|
37
|
+
|
38
|
+
unless skip_lookup
|
39
|
+
Pathname.pwd.ascend do |path|
|
40
|
+
docker_sync_files = globs_for_config(path).map { |g| Dir[g] }.flatten
|
41
|
+
break unless docker_sync_files.empty?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
files = []
|
46
|
+
files += docker_sync_files
|
47
|
+
end
|
48
|
+
|
49
|
+
# Where to look for docker-sync.yml files.
|
50
|
+
#
|
51
|
+
def globs_for_config(path)
|
52
|
+
path = escape_globs(path)
|
53
|
+
["#{path}/docker-sync.yml"]
|
54
|
+
end
|
55
|
+
|
56
|
+
def escape_globs(path)
|
57
|
+
path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&')
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'thor/shell'
|
2
|
+
require 'docker_sync/sync_process'
|
3
|
+
require 'execution'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Docker_Rsync
|
7
|
+
class SyncManager
|
8
|
+
include Thor::Shell
|
9
|
+
|
10
|
+
@sync_processes
|
11
|
+
@configurations
|
12
|
+
@config_path
|
13
|
+
def initialize(options)
|
14
|
+
@sync_processes = []
|
15
|
+
@config_syncs = []
|
16
|
+
@config_options = []
|
17
|
+
@config_syncs = []
|
18
|
+
@config_path = options[:config_path]
|
19
|
+
load_configuration
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_configuration
|
23
|
+
unless File.exist?(@config_path)
|
24
|
+
raise "Config could not be loaded from #{@config_path} - it does not exist"
|
25
|
+
end
|
26
|
+
|
27
|
+
config = YAML.load_file(@config_path)
|
28
|
+
validate_config(config)
|
29
|
+
@config_options = config['options'] || {}
|
30
|
+
@config_syncs = config['syncs']
|
31
|
+
upgrade_syncs_config
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_sync_points
|
35
|
+
return @config_syncs
|
36
|
+
end
|
37
|
+
|
38
|
+
def upgrade_syncs_config
|
39
|
+
@config_syncs.each do |name, config|
|
40
|
+
@config_syncs[name]['config_path'] = @config_path
|
41
|
+
@config_syncs[name]['src'] = File.expand_path(@config_syncs[name]['src'])
|
42
|
+
unless config.key?('verbose')
|
43
|
+
@config_syncs[name]['verbose'] = @config_options['verbose'] || false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_config(config)
|
49
|
+
unless config.key?('syncs')
|
50
|
+
raise ('no syncs defined')
|
51
|
+
end
|
52
|
+
|
53
|
+
config['syncs'].each do |name, sync_config|
|
54
|
+
validate_sync_config(name, sync_config)
|
55
|
+
end
|
56
|
+
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_sync_config(name, sync_config)
|
61
|
+
%w[src dest sync_host_port].each do |key|
|
62
|
+
raise ("#{name} does not have #{key} condiguration value set - this is mandatory") unless sync_config.key?(key)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def init_sync_processes(sync_name = nil)
|
67
|
+
if sync_name.nil?
|
68
|
+
@config_syncs.each { |name,sync_configuration|
|
69
|
+
@sync_processes.push(create_sync(name, sync_configuration))
|
70
|
+
}
|
71
|
+
else
|
72
|
+
unless @config_syncs.key?(sync_name)
|
73
|
+
raise("Could not find sync configuration with name #{sync_name}")
|
74
|
+
end
|
75
|
+
@sync_processes.push(create_sync(sync_name, @config_syncs[sync_name]))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def clean(sync_name = nil)
|
81
|
+
init_sync_processes(sync_name)
|
82
|
+
@sync_processes.each { |sync_process|
|
83
|
+
sync_process.clean
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def sync(sync_name = nil)
|
88
|
+
init_sync_processes(sync_name)
|
89
|
+
@sync_processes.each { |sync_process|
|
90
|
+
sync_process.sync
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
def run(sync_name = nil)
|
95
|
+
init_sync_processes(sync_name)
|
96
|
+
|
97
|
+
@sync_processes.each { |sync_process|
|
98
|
+
sync_process.run
|
99
|
+
}
|
100
|
+
|
101
|
+
begin
|
102
|
+
@sync_processes.each do |sync_process|
|
103
|
+
sync_process.watch_thread.join
|
104
|
+
end
|
105
|
+
|
106
|
+
rescue SystemExit, Interrupt
|
107
|
+
|
108
|
+
puts "Shutting down..."
|
109
|
+
@sync_processes.each do |sync_process|
|
110
|
+
sync_process.stop
|
111
|
+
end
|
112
|
+
@sync_processes.each do |sync_process|
|
113
|
+
sync_process.watch_thread.kill
|
114
|
+
end
|
115
|
+
|
116
|
+
rescue Exception => e
|
117
|
+
|
118
|
+
puts "EXCEPTION: #{e.inspect}"
|
119
|
+
puts "MESSAGE: #{e.message}"
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def create_sync(sync_name, sync_configuration)
|
125
|
+
sync_process = Docker_Sync::SyncProcess.new(sync_name, sync_configuration)
|
126
|
+
return sync_process
|
127
|
+
end
|
128
|
+
|
129
|
+
def stop
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'thor/shell'
|
2
|
+
require 'execution'
|
3
|
+
require 'shellwords'
|
4
|
+
module Docker_Sync
|
5
|
+
class SyncProcess
|
6
|
+
include Thor::Shell
|
7
|
+
include Execution
|
8
|
+
@options
|
9
|
+
@sync_name
|
10
|
+
@watch_thread
|
11
|
+
|
12
|
+
def initialize(sync_name, options)
|
13
|
+
defaults = {
|
14
|
+
'verbose' => false,
|
15
|
+
'sync_host_ip' => get_host_ip
|
16
|
+
}
|
17
|
+
@sync_name = sync_name
|
18
|
+
@options = defaults.merge(options)
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_host_ip
|
23
|
+
return 'localhost'
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
start_container
|
28
|
+
sync
|
29
|
+
watch
|
30
|
+
end
|
31
|
+
|
32
|
+
def stop
|
33
|
+
say_status 'ok', "Stopping sync container #{@sync_name}"
|
34
|
+
begin
|
35
|
+
`docker stop #{@sync_name}`
|
36
|
+
rescue Exception => e
|
37
|
+
say_status 'error', "Stopping failed of #{@sync_name}:", :red
|
38
|
+
puts e.message
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def start_container
|
43
|
+
say_status 'ok', 'Starting rsync', :white
|
44
|
+
running = `docker ps --filter 'status=running' --filter 'name=#{@sync_name}' | grep #{@sync_name}`
|
45
|
+
if running == ''
|
46
|
+
say_status 'ok', "#{@sync_name} container not running", :white
|
47
|
+
exists = `docker ps --filter "status=exited" --filter "name=filesync_dw" | grep filesync_dw`
|
48
|
+
if exists == ''
|
49
|
+
say_status 'ok', "creating #{@sync_name} container", :white
|
50
|
+
cmd = "docker run -p '#{@options['sync_host_port']}:873' -v #{@sync_name}:#{@options['dest']} -e VOLUME=#{@options['dest']} --name #{@sync_name} -d eugenmayer/rsync"
|
51
|
+
else
|
52
|
+
say_status 'success', "starting #{@sync_name} container", :green
|
53
|
+
cmd = "docker start #{@sync_name}"
|
54
|
+
end
|
55
|
+
say_status 'command', cmd, :white
|
56
|
+
`#{cmd}` || raise('Start failed')
|
57
|
+
else
|
58
|
+
say_status 'ok', "#{@sync_name} container still running", :blue
|
59
|
+
end
|
60
|
+
say_status 'success', "starting initial #{@sync_name} of src", :green
|
61
|
+
# this sleep is needed since the container could be not started
|
62
|
+
sleep 1
|
63
|
+
sync
|
64
|
+
end
|
65
|
+
|
66
|
+
def stop_container
|
67
|
+
`docker stop #{@sync_name}`
|
68
|
+
end
|
69
|
+
|
70
|
+
def reset_container
|
71
|
+
stop_container
|
72
|
+
`docker rm #{@sync_name}`
|
73
|
+
`docker volume rm #{@sync_name}`
|
74
|
+
end
|
75
|
+
|
76
|
+
def clean
|
77
|
+
reset_container
|
78
|
+
end
|
79
|
+
|
80
|
+
def sync
|
81
|
+
args = sync_options
|
82
|
+
cmd = 'rsync ' + args.join(' ')
|
83
|
+
|
84
|
+
say_status 'command', cmd, :white if @options['verbose']
|
85
|
+
|
86
|
+
out = `#{cmd}`
|
87
|
+
if $?.exitstatus > 0
|
88
|
+
say_status 'error', "Error starting sync, exit code #{$?.exitstatus}", :red
|
89
|
+
say_status 'message', out
|
90
|
+
else
|
91
|
+
say_status 'success', "Synced #{@options['src']}", :green
|
92
|
+
if @options['verbose']
|
93
|
+
say_status 'output', out
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def sync_options
|
99
|
+
args = []
|
100
|
+
unless @options['sync_excludes'].nil?
|
101
|
+
args = @options['sync_excludes'].map { |pattern| "--exclude='#{pattern}'" } + args
|
102
|
+
end
|
103
|
+
args.push('-ap')
|
104
|
+
args.push(@options['sync_args']) if @options.key?('sync_args')
|
105
|
+
args.push("#{@options['src']}/") # add a trailing slash
|
106
|
+
args.push("rsync://#{@options['sync_host_ip']}:#{@options['sync_host_port']}/volume")
|
107
|
+
end
|
108
|
+
|
109
|
+
def watch
|
110
|
+
args = watch_options
|
111
|
+
say_status 'ok', "Starting to watch #{@options['src']} - Press CTRL-C to stop", :green
|
112
|
+
cmd = 'fswatch ' + args.join(' ')
|
113
|
+
say_status 'command', cmd, :white if @options['verbose']
|
114
|
+
|
115
|
+
@watch_thread = threadexec(cmd, "Sync #{@sync_name}", :blue)
|
116
|
+
end
|
117
|
+
|
118
|
+
def watch_options
|
119
|
+
args = []
|
120
|
+
unless @options['watch_excludes'].nil?
|
121
|
+
args = @options['watch_excludes'].map { |pattern| "--exclude='#{pattern}'" } + args
|
122
|
+
end
|
123
|
+
args.push('-orIE')
|
124
|
+
args.push(@options['watch_args']) if @options.key?('watch_args')
|
125
|
+
args.push(@options['src'])
|
126
|
+
args.push(" | xargs -I -n1 thor sync:sync -n #{@sync_name} --config='#{@options['config_path']}'")
|
127
|
+
end
|
128
|
+
|
129
|
+
def watch_thread
|
130
|
+
return @watch_thread
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/lib/execution.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
module Execution
|
5
|
+
|
6
|
+
Thread.abort_on_exception = true
|
7
|
+
|
8
|
+
def threadexec(command, prefix = nil, color = nil)
|
9
|
+
|
10
|
+
unless prefix.nil?
|
11
|
+
prefix = "#{prefix} | "
|
12
|
+
end
|
13
|
+
|
14
|
+
if color.nil?
|
15
|
+
color = :cyan
|
16
|
+
end
|
17
|
+
|
18
|
+
Thread.new {
|
19
|
+
Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
|
20
|
+
|
21
|
+
while lineOut = stdout.gets
|
22
|
+
puts prefix.nil? ? lineOut : prefix.colorize(color) + lineOut
|
23
|
+
end
|
24
|
+
|
25
|
+
while lineErr = stderr.gets
|
26
|
+
puts prefix.nil? ? lineErr : prefix.colorize(color) + lineErr
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
}
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/tasks/sync.thor
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'docker_sync/sync_manager'
|
2
|
+
require 'config'
|
3
|
+
|
4
|
+
class Sync < Thor
|
5
|
+
include DockerSyncConfig
|
6
|
+
class_option :config, :aliases => '-c',:default => nil, :type => :string, :desc => 'Path of the docker_sync config'
|
7
|
+
class_option :sync_name, :aliases => '-n',:type => :string, :desc => 'If given, only this sync configuration will be references/started/synced'
|
8
|
+
|
9
|
+
desc 'start', 'Start all sync configurations in this project'
|
10
|
+
def start
|
11
|
+
if options[:config]
|
12
|
+
config_path = options[:config]
|
13
|
+
else
|
14
|
+
begin
|
15
|
+
config_path = find_config
|
16
|
+
rescue Exception => e
|
17
|
+
say_status 'error', e.message, :red
|
18
|
+
return
|
19
|
+
end
|
20
|
+
end
|
21
|
+
@sync_manager = Docker_Rsync::SyncManager.new(:config_path => config_path)
|
22
|
+
@sync_manager.run(options[:sync_name])
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'sync_only', 'sync - do not start a watcher'
|
26
|
+
def sync
|
27
|
+
if options[:config]
|
28
|
+
config_path = options[:config]
|
29
|
+
else
|
30
|
+
begin
|
31
|
+
config_path = find_config
|
32
|
+
rescue Exception => e
|
33
|
+
say_status 'error', e.message, :red
|
34
|
+
return
|
35
|
+
end
|
36
|
+
end
|
37
|
+
@sync_manager = Docker_Rsync::SyncManager.new(:config_path => config_path)
|
38
|
+
@sync_manager.sync(options[:sync_name])
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'clean', 'Stop and clean up all sync endpoints'
|
42
|
+
def clean
|
43
|
+
if options[:config]
|
44
|
+
config_path = options[:config]
|
45
|
+
else
|
46
|
+
begin
|
47
|
+
config_path = find_config
|
48
|
+
rescue Exception => e
|
49
|
+
say_status 'error', e.message, :red
|
50
|
+
return
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@sync_manager = Docker_Rsync::SyncManager.new(:config_path => config_path)
|
54
|
+
@sync_manager.clean(options[:sync_name])
|
55
|
+
say_status 'success', 'Finished cleanup. Removed stopped, removed sync container and removed there volumes', :green
|
56
|
+
end
|
57
|
+
|
58
|
+
desc 'list', 'List all sync-points of the project configuration path'
|
59
|
+
method_option :verbose, :default => false, :type => :boolean, :desc => 'Verbose output'
|
60
|
+
def list
|
61
|
+
if options[:config]
|
62
|
+
config_path = options[:config]
|
63
|
+
else
|
64
|
+
begin
|
65
|
+
config_path = find_config
|
66
|
+
rescue Exception => e
|
67
|
+
say_status 'error', e.message, :red
|
68
|
+
return
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
say_status 'ok',"Found configuration at #{config_path}"
|
73
|
+
@sync_manager = Docker_Rsync::SyncManager.new(:config_path => config_path)
|
74
|
+
@sync_manager.get_sync_points.each do |name, config|
|
75
|
+
say_status name, "On address #{config['sync_host_ip']}:#{config['sync_host_port']}",:white unless options['verbose']
|
76
|
+
puts "\n---------------[#{name}] #{config['sync_host_ip']}:#{config['sync_host_port']} ---------------\n" if options['verbose']
|
77
|
+
print_table(config) if options['verbose']
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: docker-sync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eugen Mayer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: colorize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Sync your code live to docker-containers without losing any performance
|
42
|
+
on OSX
|
43
|
+
email: eugen.mayer@kontextwork.de
|
44
|
+
executables:
|
45
|
+
- docker-sync
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- Thorfile
|
50
|
+
- bin/docker-sync
|
51
|
+
- lib/config.rb
|
52
|
+
- lib/docker_sync/sync_manager.rb
|
53
|
+
- lib/docker_sync/sync_process.rb
|
54
|
+
- lib/execution.rb
|
55
|
+
- tasks/sync.thor
|
56
|
+
homepage: https://github.com/EugenMayer/docker_sync
|
57
|
+
licenses:
|
58
|
+
- GPL
|
59
|
+
metadata: {}
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 2.4.5.1
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: Docker Sync - Fast and efficient way to sync code to docker-containers
|
80
|
+
test_files: []
|