docker-sync 0.0.13 → 0.0.14
Sign up to get free protection for your applications and to get access to all the features.
- 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
|