docker-sync 0.0.13 → 0.0.14
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 +4 -4
- data/Thorfile +4 -3
- data/VERSION +1 -1
- data/bin/docker-sync +3 -2
- data/bin/docker-sync-stack +3 -2
- data/lib/docker-sync/sync_manager.rb +13 -1
- data/lib/docker-sync/sync_process.rb +4 -3
- data/lib/docker-sync/sync_strategy/unison-dualside.rb +199 -0
- data/lib/docker-sync/sync_strategy/unison.rb +1 -1
- data/lib/docker-sync/watch_strategy/fswatch.rb +7 -6
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a467b9dee71d933110ea66148ac480c7ed3d9b0
|
4
|
+
data.tar.gz: 1533c522f6aa5db93e15d85e4b8a53f61f350e2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3dfb30f05807f691089c1e106934a29e2bfa54794994b6d7180789824052e1f18532e239058b91f1989b8779c27bbfe58a2adf7db6eaa7c06c1ebc72b2b41004
|
7
|
+
data.tar.gz: c09c3b2a2e6615302bf5593e6f5472fa7f364a96e3183a421c09d911042fd20bad5ae0a2e83005f821b0e2883eebd15d4307aa178564b28f1256119b94785c5a
|
data/Thorfile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
2
|
+
file = File.expand_path(File.dirname(file))
|
3
|
+
$LOAD_PATH.unshift File.expand_path('./lib', file)
|
4
|
+
thor = File.expand_path('./tasks', file)
|
4
5
|
|
5
6
|
Dir.glob(File.join(thor, '/**/*.thor')).each { |taskfile|
|
6
7
|
load taskfile
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.14
|
data/bin/docker-sync
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
3
|
-
|
3
|
+
file = File.expand_path(File.dirname(file))
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../lib', file)
|
4
5
|
|
5
6
|
require 'thor'
|
6
7
|
require 'docker-sync/update_check'
|
7
8
|
|
8
|
-
thor = File.expand_path('
|
9
|
+
thor = File.expand_path('../tasks/sync', file)
|
9
10
|
Dir.glob(File.join(thor, '/**/*.thor')).each { |taskfile|
|
10
11
|
load taskfile
|
11
12
|
}
|
data/bin/docker-sync-stack
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
3
|
-
|
3
|
+
file = File.expand_path(File.dirname(file))
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../lib', file)
|
4
5
|
|
5
6
|
|
6
7
|
require 'thor'
|
@@ -16,7 +17,7 @@ require 'docker-sync/update_check'
|
|
16
17
|
# exit 0
|
17
18
|
#end
|
18
19
|
|
19
|
-
thor = File.expand_path('
|
20
|
+
thor = File.expand_path('../tasks/stack', file)
|
20
21
|
Dir.glob(File.join(thor, '/**/*.thor')).each { |taskfile|
|
21
22
|
# noinspection RubyResolve
|
22
23
|
load taskfile
|
@@ -45,7 +45,7 @@ module Docker_Rsync
|
|
45
45
|
@config_syncs.each do |name, config|
|
46
46
|
@config_syncs[name]['config_path'] = @config_path
|
47
47
|
# expand the sync source to remove ~ and similar expressions in the path
|
48
|
-
@config_syncs[name]['src'] = File.expand_path(@config_syncs[name]['src'])
|
48
|
+
@config_syncs[name]['src'] = File.expand_path(@config_syncs[name]['src'], File.dirname(@config_path))
|
49
49
|
|
50
50
|
# set the global verbose setting, if the sync-endpoint does not define a own one
|
51
51
|
unless config.key?('verbose')
|
@@ -150,5 +150,17 @@ module Docker_Rsync
|
|
150
150
|
end
|
151
151
|
}
|
152
152
|
end
|
153
|
+
|
154
|
+
def watch_stop
|
155
|
+
@sync_processes.each { |sync_process|
|
156
|
+
sync_process.watch_thread.kill unless sync_process.watch_thread.nil?
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
def watch_start
|
161
|
+
@sync_processes.each { |sync_process|
|
162
|
+
sync_process.watch
|
163
|
+
}
|
164
|
+
end
|
153
165
|
end
|
154
166
|
end
|
@@ -2,6 +2,7 @@ require 'thor/shell'
|
|
2
2
|
# noinspection RubyResolve
|
3
3
|
require 'docker-sync/sync_strategy/rsync'
|
4
4
|
require 'docker-sync/sync_strategy/unison'
|
5
|
+
require 'docker-sync/sync_strategy/unison-dualside'
|
5
6
|
# noinspection RubyResolve
|
6
7
|
require 'docker-sync/watch_strategy/fswatch'
|
7
8
|
require 'docker-sync/watch_strategy/dummy'
|
@@ -36,6 +37,8 @@ module Docker_Sync
|
|
36
37
|
@sync_strategy = Docker_Sync::SyncStrategy::Rsync.new(@sync_name, @options)
|
37
38
|
when 'unison'
|
38
39
|
@sync_strategy = Docker_Sync::SyncStrategy::Unison.new(@sync_name, @options)
|
40
|
+
when 'unison-dualside'
|
41
|
+
@sync_strategy = Docker_Sync::SyncStrategy::Unison_DualSide.new(@sync_name, @options)
|
39
42
|
else
|
40
43
|
@sync_strategy = Docker_Sync::SyncStrategy::Rsync.new(@sync_name, @options)
|
41
44
|
end
|
@@ -49,9 +52,7 @@ module Docker_Sync
|
|
49
52
|
case @options['watch_strategy']
|
50
53
|
when 'fswatch'
|
51
54
|
@watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
|
52
|
-
when 'disable'
|
53
|
-
@watch_strategy = Docker_Sync::WatchStrategy::Dummy.new(@sync_name, @options)
|
54
|
-
when 'dummy'
|
55
|
+
when 'disable','dummy'
|
55
56
|
@watch_strategy = Docker_Sync::WatchStrategy::Dummy.new(@sync_name, @options)
|
56
57
|
else
|
57
58
|
@watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
|
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'thor/shell'
|
2
|
+
require 'docker-sync/preconditions'
|
3
|
+
require 'open3'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
module Docker_Sync
|
7
|
+
module SyncStrategy
|
8
|
+
class Unison_DualSide
|
9
|
+
include Thor::Shell
|
10
|
+
|
11
|
+
@options
|
12
|
+
@sync_name
|
13
|
+
@watch_thread
|
14
|
+
@local_server_pid
|
15
|
+
UNISON_CONTAINER_PORT = '5000'
|
16
|
+
def initialize(sync_name, options)
|
17
|
+
@sync_name = sync_name
|
18
|
+
@options = options
|
19
|
+
# if a custom image is set, apply it
|
20
|
+
if @options.key?('image')
|
21
|
+
@docker_image = @options['image']
|
22
|
+
else
|
23
|
+
@docker_image = 'eugenmayer/unison:dualside'
|
24
|
+
end
|
25
|
+
begin
|
26
|
+
Preconditions::unison_available
|
27
|
+
rescue Exception => e
|
28
|
+
say_status 'error', "#{@sync_name} has been configured to sync with unison, but no unison available", :red
|
29
|
+
say_status 'error', e.message, :red
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def run
|
35
|
+
start_container
|
36
|
+
start_local_server
|
37
|
+
sync
|
38
|
+
end
|
39
|
+
|
40
|
+
def sync
|
41
|
+
args = sync_options
|
42
|
+
cmd = 'unison ' + args.join(' ')
|
43
|
+
|
44
|
+
say_status 'command', cmd, :white if @options['verbose']
|
45
|
+
|
46
|
+
stdout, stderr, exit_status = Open3.capture3(cmd)
|
47
|
+
if not exit_status.success?
|
48
|
+
say_status 'error', "Error starting sync, exit code #{$?.exitstatus}", :red
|
49
|
+
say_status 'message', stderr
|
50
|
+
else
|
51
|
+
say_status 'ok', "Synced #{@options['src']}", :white
|
52
|
+
if @options['verbose']
|
53
|
+
say_status 'output', stdout
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def sync_options
|
59
|
+
args = []
|
60
|
+
|
61
|
+
unless @options['sync_excludes'].nil?
|
62
|
+
args = @options['sync_excludes'].map { |pattern| "-ignore='Path #{pattern}'" } + args
|
63
|
+
end
|
64
|
+
args.push(@options['src'])
|
65
|
+
args.push('-auto')
|
66
|
+
args.push('-batch')
|
67
|
+
args.push('-owner') if @options['sync_userid'] == 'from_host'
|
68
|
+
args.push('-numericids') if @options['sync_userid'] == 'from_host'
|
69
|
+
args.push(@options['sync_args']) if @options.key?('sync_args')
|
70
|
+
args.push("socket://#{@options['sync_host_ip']}:#{@options['sync_host_port']}/")
|
71
|
+
args.push('-debug all') if @options['verbose']
|
72
|
+
if @options.key?('sync_user') || @options.key?('sync_group') || @options.key?('sync_groupid')
|
73
|
+
raise('Unison does not support sync_user, sync_group, sync_groupid - please use rsync if you need that')
|
74
|
+
end
|
75
|
+
if @options.key?('sync_userid') && @options['sync_userid'] != 'from_host'
|
76
|
+
raise('Unison does not support sync_userid with a parameter different than \'from_host\'')
|
77
|
+
end
|
78
|
+
return args
|
79
|
+
end
|
80
|
+
|
81
|
+
def start_container
|
82
|
+
say_status 'ok', 'Starting unison', :white
|
83
|
+
container_name = get_container_name
|
84
|
+
volume_name = get_volume_name
|
85
|
+
env = {}
|
86
|
+
|
87
|
+
env['ENABLE_WATCH'] = 'true'
|
88
|
+
# Excludes for fswatch and unison uses the same value both in the host and in the container
|
89
|
+
env['FSWATCH_EXCLUDES'] = @options['watch_excludes'].map { |pattern| "--exclude='#{pattern}'" }.join(' ') if @options.key?('watch_excludes')
|
90
|
+
env['UNISON_EXCLUDES'] = @options['sync_excludes'].map { |pattern| "-ignore='Path #{pattern}'" }.join(' ') if @options.key?('sync_excludes')
|
91
|
+
# Specify the IP of the host to call the host unison server from the container
|
92
|
+
env['HOST_IP'] = get_host_ip
|
93
|
+
env['UNISON_HOST_PORT'] = @options['unison-dualside_host_server_port']
|
94
|
+
|
95
|
+
if @options['sync_userid'] == 'from_host'
|
96
|
+
env['UNISON_DIR_OWNER'] = Process.uid
|
97
|
+
end
|
98
|
+
|
99
|
+
additional_docker_env = env.map{ |key,value| "-e #{key}=\"#{value}\"" }.join(' ')
|
100
|
+
running = `docker ps --filter 'status=running' --filter 'name=#{container_name}' | grep #{container_name}`
|
101
|
+
if running == ''
|
102
|
+
say_status 'ok', "#{container_name} container not running", :white if @options['verbose']
|
103
|
+
exists = `docker ps --filter "status=exited" --filter "name=#{container_name}" | grep #{container_name}`
|
104
|
+
if exists == ''
|
105
|
+
say_status 'ok', "creating #{container_name} container", :white if @options['verbose']
|
106
|
+
cmd = "docker run -p '#{@options['sync_host_port']}:#{UNISON_CONTAINER_PORT}' \
|
107
|
+
-v #{volume_name}:#{@options['dest']} \
|
108
|
+
-e UNISON_DIR=#{@options['dest']} \
|
109
|
+
#{additional_docker_env} \
|
110
|
+
--name #{container_name} \
|
111
|
+
-d #{@docker_image}"
|
112
|
+
else
|
113
|
+
say_status 'ok', "starting #{container_name} container", :white if @options['verbose']
|
114
|
+
cmd = "docker start #{container_name}"
|
115
|
+
end
|
116
|
+
say_status 'command', cmd, :white if @options['verbose']
|
117
|
+
`#{cmd}` || raise('Start failed')
|
118
|
+
else
|
119
|
+
say_status 'ok', "#{container_name} container still running", :blue
|
120
|
+
end
|
121
|
+
say_status 'ok', "starting initial #{container_name} of src", :white if @options['verbose']
|
122
|
+
# this sleep is needed since the container could be not started
|
123
|
+
sleep 5 # TODO: replace with unison -testserver
|
124
|
+
sync
|
125
|
+
say_status 'success', 'Unison server started', :green
|
126
|
+
end
|
127
|
+
|
128
|
+
# Start an unison server on the host
|
129
|
+
def start_local_server
|
130
|
+
say_status 'ok', 'Starting local unison server', :white
|
131
|
+
|
132
|
+
if not @options.key?('unison-dualside_host_server_port')
|
133
|
+
raise('Missing unison-dualside_host_server_port option. In order to get files synced back automatically from the
|
134
|
+
container, you must specify the port of the local unison server.')
|
135
|
+
end
|
136
|
+
cmd = "unison -socket #{@options['unison-dualside_host_server_port']}"
|
137
|
+
say_status 'command', cmd, :white if @options['verbose']
|
138
|
+
|
139
|
+
# Store the pid of the local unison server, to kill it when docker-sync stops
|
140
|
+
@local_server_pid = Process.fork do
|
141
|
+
# Run the local unison server in the source folder
|
142
|
+
Dir.chdir(@options['src']){
|
143
|
+
`#{cmd}` || raise('Start of local unison server failed')
|
144
|
+
}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Try to guess the IP of the host to call the unison server from the container
|
149
|
+
# Or use the value specified in the config option
|
150
|
+
def get_host_ip
|
151
|
+
if @options['unison-dualside_host_ip'] != 'auto' && @options.key?('unison-dualside_host_ip')
|
152
|
+
host_ip = @options['host_ip']
|
153
|
+
elsif @options['unison-dualside_host_ip'] == 'auto' || (not @options.key?('unison-dualside_host_ip'))
|
154
|
+
host_ip = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address
|
155
|
+
end
|
156
|
+
return host_ip
|
157
|
+
end
|
158
|
+
|
159
|
+
# Kill the local unison server
|
160
|
+
def stop_local_server
|
161
|
+
Process.kill "TERM", @local_server_pid
|
162
|
+
Process.wait @local_server_pid
|
163
|
+
end
|
164
|
+
|
165
|
+
def get_container_name
|
166
|
+
return "#{@sync_name}"
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_volume_name
|
170
|
+
return @sync_name
|
171
|
+
end
|
172
|
+
|
173
|
+
def stop_container
|
174
|
+
`docker stop #{get_container_name}`
|
175
|
+
end
|
176
|
+
|
177
|
+
def reset_container
|
178
|
+
stop_container
|
179
|
+
`docker rm #{get_container_name}`
|
180
|
+
`docker volume rm #{get_volume_name}`
|
181
|
+
end
|
182
|
+
|
183
|
+
def clean
|
184
|
+
reset_container
|
185
|
+
end
|
186
|
+
|
187
|
+
def stop
|
188
|
+
say_status 'ok', "Stopping sync container #{get_container_name}"
|
189
|
+
begin
|
190
|
+
stop_container
|
191
|
+
stop_local_server
|
192
|
+
rescue Exception => e
|
193
|
+
say_status 'error', "Stopping failed of #{get_container_name}:", :red
|
194
|
+
puts e.message
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -63,7 +63,7 @@ module Docker_Sync
|
|
63
63
|
args.push('-batch')
|
64
64
|
args.push(@options['sync_args']) if @options.key?('sync_args')
|
65
65
|
args.push("socket://#{@options['sync_host_ip']}:#{@options['sync_host_port']}/")
|
66
|
-
args.push('-debug
|
66
|
+
args.push('-debug all') if @options['verbose']
|
67
67
|
if @options.key?('sync_user') || @options.key?('sync_group') || @options.key?('sync_groupid') || @options.key?('sync_userid')
|
68
68
|
raise('Unison does not support sync_user, sync_group, sync_groupid or sync_userid - please use rsync if you need that')
|
69
69
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'thor/shell'
|
2
2
|
require 'docker-sync/execution'
|
3
3
|
require 'docker-sync/preconditions'
|
4
|
+
require 'pathname'
|
4
5
|
|
5
6
|
module Docker_Sync
|
6
7
|
module WatchStrategy
|
@@ -15,6 +16,7 @@ module Docker_Sync
|
|
15
16
|
def initialize(sync_name, options)
|
16
17
|
@sync_name = sync_name
|
17
18
|
@options = options
|
19
|
+
@events_to_watch = %w(AttributeModified Created Link MovedFrom MovedTo Renamed Removed Updated)
|
18
20
|
|
19
21
|
begin
|
20
22
|
Preconditions::fswatch_available
|
@@ -50,17 +52,16 @@ module Docker_Sync
|
|
50
52
|
args = @options['watch_excludes'].map { |pattern| "--exclude='#{pattern}'" } + args
|
51
53
|
end
|
52
54
|
args.push('-orIE')
|
55
|
+
args.push(@events_to_watch.map { |pattern| "--event #{pattern}" })
|
53
56
|
args.push(@options['watch_args']) if @options.key?('watch_args')
|
54
57
|
args.push(@options['src'])
|
55
58
|
|
56
59
|
sync_command = 'thor sync:sync'
|
57
|
-
|
58
|
-
|
60
|
+
exec_name =File.basename($PROGRAM_NAME)
|
61
|
+
if exec_name != 'thor'
|
59
62
|
sync_command = 'docker-sync sync'
|
60
|
-
|
61
|
-
say_status 'warning', 'docker-sync
|
62
|
-
puts e.message
|
63
|
-
sync_command = 'thor sync:sync'
|
63
|
+
else
|
64
|
+
say_status 'warning', 'Called user thor, not docker-sync* wise, assuming dev mode, using thor', :yellow
|
64
65
|
end
|
65
66
|
args.push(" | xargs -I -n1 #{sync_command} -n #{@sync_name} --config='#{@options['config_path']}'")
|
66
67
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: docker-sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eugen Mayer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-07-
|
11
|
+
date: 2016-07-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- lib/docker-sync/sync_manager.rb
|
85
85
|
- lib/docker-sync/sync_process.rb
|
86
86
|
- lib/docker-sync/sync_strategy/rsync.rb
|
87
|
+
- lib/docker-sync/sync_strategy/unison-dualside.rb
|
87
88
|
- lib/docker-sync/sync_strategy/unison.rb
|
88
89
|
- lib/docker-sync/update_check.rb
|
89
90
|
- lib/docker-sync/watch_strategy/dummy.rb
|