docker-sync 0.0.15 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/docker-sync/execution.rb +19 -0
- data/lib/docker-sync/preconditions.rb +17 -1
- data/lib/docker-sync/sync_manager.rb +10 -3
- data/lib/docker-sync/sync_process.rb +15 -4
- data/lib/docker-sync/sync_strategy/rsync.rb +3 -3
- data/lib/docker-sync/sync_strategy/{unison-dualside.rb → unison-onesided.rb} +8 -74
- data/lib/docker-sync/sync_strategy/unison.rb +84 -15
- data/lib/docker-sync/update_check.rb +26 -0
- data/lib/docker-sync/upgrade_check.rb +59 -0
- data/lib/docker-sync/watch_strategy/unison.rb +53 -0
- data/tasks/stack/stack.thor +6 -2
- data/tasks/sync/sync.thor +13 -5
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6cb5e3a077d9de57cad162afbdab55c0aa2ed36f
|
4
|
+
data.tar.gz: 73b3b75811e54265c180c4e6cc540a23f762a394
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 420ff17cc42f4d5422e85604f5e28b1ff10e6a820435130bca44dccf6f970c349583d068195ca5f460a9c89b230ad94f255f56dac03f9b79b95036c54cb60eed
|
7
|
+
data.tar.gz: 0d1284902dcb5b72fe1cde3c28f9279efba62db2990f2c9c9837e3461b8f0032f2c61b6b2f1b588ba4fc6936cf220b195be25b3c3221d17d7e952674a77e6d61
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
@@ -34,4 +34,23 @@ module Execution
|
|
34
34
|
|
35
35
|
end
|
36
36
|
|
37
|
+
# unison doesn't work when ran in a new thread
|
38
|
+
# this functions creates a full new process instead
|
39
|
+
def forkexec(command, prefix = nil, color = nil)
|
40
|
+
|
41
|
+
if prefix.nil?
|
42
|
+
# TODO: probably pick the command name without args
|
43
|
+
prefix = 'unknown'
|
44
|
+
end
|
45
|
+
|
46
|
+
if color.nil?
|
47
|
+
color = :cyan
|
48
|
+
end
|
49
|
+
|
50
|
+
Process.fork {
|
51
|
+
`#{command}` || raise(command + ' failed')
|
52
|
+
}
|
53
|
+
|
54
|
+
end
|
55
|
+
|
37
56
|
end
|
@@ -43,4 +43,20 @@ module Preconditions
|
|
43
43
|
raise('Could not find unison binary in path. Please install it, e.g. using "brew install unison"')
|
44
44
|
end
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
|
+
def self.unox_available
|
48
|
+
if (find_executable0 'unison-fsmonitor').nil?
|
49
|
+
cmd1 = 'curl "https://raw.githubusercontent.com/hnsl/unox/master/unox.py" -o "/usr/local/bin/unison-fsmonitor" \
|
50
|
+
&& chmod +x /usr/local/bin/unison-fsmonitor'
|
51
|
+
cmd2 = 'easy_install pip && pip install macfsevents'
|
52
|
+
Thor::Shell::Basic.new.say_status 'warning', 'Could not find unison-fsmonitor (for file watching) binary in $PATH. Please install unox before you continue, see https://github.com/hnsl/unox.', :yellow
|
53
|
+
if Thor::Shell::Basic.new.yes?('Shall I install unison-fsmonitor for you?')
|
54
|
+
`#{cmd1}`
|
55
|
+
Thor::Shell::Basic.new.say_status 'ok','install macfsevents using pip', :yellow
|
56
|
+
`#{cmd2}`
|
57
|
+
else
|
58
|
+
raise("Please install it, see https://github.com/hnsl/unox, or simply run :\n #{cmd1} && #{cmd2}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -5,7 +5,7 @@ require 'docker-sync/sync_process'
|
|
5
5
|
require 'docker-sync/execution'
|
6
6
|
require 'yaml'
|
7
7
|
|
8
|
-
module
|
8
|
+
module Docker_sync
|
9
9
|
class SyncManager
|
10
10
|
include Thor::Shell
|
11
11
|
|
@@ -79,7 +79,9 @@ module Docker_Rsync
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def validate_sync_config(name, sync_config)
|
82
|
-
%w[src dest
|
82
|
+
config_mandatory = %w[src dest]
|
83
|
+
config_mandatory.push('sync_host_port') unless sync_config['sync_strategy'] == 'unison' #TODO: Implement autodisovery for other strategies
|
84
|
+
config_mandatory.each do |key|
|
83
85
|
raise ("#{name} does not have #{key} condiguration value set - this is mandatory") unless sync_config.key?(key)
|
84
86
|
end
|
85
87
|
end
|
@@ -123,7 +125,12 @@ module Docker_Rsync
|
|
123
125
|
def join_threads
|
124
126
|
begin
|
125
127
|
@sync_processes.each do |sync_process|
|
126
|
-
|
128
|
+
if sync_process.watch_thread
|
129
|
+
sync_process.watch_thread.join
|
130
|
+
end
|
131
|
+
if sync_process.watch_fork
|
132
|
+
Process.wait(sync_process.watch_fork)
|
133
|
+
end
|
127
134
|
end
|
128
135
|
|
129
136
|
rescue SystemExit, Interrupt
|
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'thor/shell'
|
2
2
|
# noinspection RubyResolve
|
3
3
|
require 'docker-sync/sync_strategy/rsync'
|
4
|
+
require 'docker-sync/sync_strategy/unison-onesided'
|
4
5
|
require 'docker-sync/sync_strategy/unison'
|
5
|
-
require 'docker-sync/sync_strategy/unison-dualside'
|
6
6
|
# noinspection RubyResolve
|
7
7
|
require 'docker-sync/watch_strategy/fswatch'
|
8
8
|
require 'docker-sync/watch_strategy/dummy'
|
9
|
+
require 'docker-sync/watch_strategy/unison'
|
9
10
|
|
10
11
|
module Docker_Sync
|
11
12
|
class SyncProcess
|
@@ -35,10 +36,10 @@ module Docker_Sync
|
|
35
36
|
case @options['sync_strategy']
|
36
37
|
when 'rsync'
|
37
38
|
@sync_strategy = Docker_Sync::SyncStrategy::Rsync.new(@sync_name, @options)
|
39
|
+
when 'unison-onesided'
|
40
|
+
@sync_strategy = Docker_Sync::SyncStrategy::Unison_Onesided.new(@sync_name, @options)
|
38
41
|
when 'unison'
|
39
42
|
@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)
|
42
43
|
else
|
43
44
|
@sync_strategy = Docker_Sync::SyncStrategy::Rsync.new(@sync_name, @options)
|
44
45
|
end
|
@@ -54,11 +55,17 @@ module Docker_Sync
|
|
54
55
|
@watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
|
55
56
|
when 'disable','dummy'
|
56
57
|
@watch_strategy = Docker_Sync::WatchStrategy::Dummy.new(@sync_name, @options)
|
58
|
+
when 'unison'
|
59
|
+
@watch_strategy = Docker_Sync::WatchStrategy::Unison.new(@sync_name, @options)
|
57
60
|
else
|
58
61
|
@watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
|
59
62
|
end
|
60
63
|
else
|
61
|
-
@
|
64
|
+
if @options['sync_strategy'] == 'unison'
|
65
|
+
@watch_strategy = Docker_Sync::WatchStrategy::Unison.new(@sync_name, @options)
|
66
|
+
else
|
67
|
+
@watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
|
68
|
+
end
|
62
69
|
end
|
63
70
|
end
|
64
71
|
|
@@ -90,6 +97,10 @@ module Docker_Sync
|
|
90
97
|
@watch_strategy.run
|
91
98
|
end
|
92
99
|
|
100
|
+
def watch_fork
|
101
|
+
return @watch_strategy.watch_fork
|
102
|
+
end
|
103
|
+
|
93
104
|
def watch_thread
|
94
105
|
return @watch_strategy.watch_thread
|
95
106
|
end
|
@@ -87,17 +87,17 @@ module Docker_Sync
|
|
87
87
|
say_status 'ok', 'Starting rsync', :white
|
88
88
|
container_name = get_container_name
|
89
89
|
volume_name = get_volume_name
|
90
|
-
running = `docker ps --filter 'status=running' --filter 'name=#{container_name}' | grep
|
90
|
+
running = `docker ps --filter 'status=running' --filter 'name=#{container_name}' | sed -E -n 's/.*\\s(.*)$/\\1/p' | grep '^#{container_name}$'`
|
91
91
|
if running == '' # container is yet not running
|
92
92
|
say_status 'ok', "#{container_name} container not running", :white if @options['verbose']
|
93
|
-
exists = `docker ps --filter "status=exited" --filter "name=#{container_name}" | grep
|
93
|
+
exists = `docker ps --filter "status=exited" --filter "name=#{container_name}" | sed -E -n 's/.*\\s(.*)$/\\1/p' | grep '^#{container_name}$'`
|
94
94
|
if exists == '' # container has yet not been created
|
95
95
|
say_status 'ok', "creating #{container_name} container", :white if @options['verbose']
|
96
96
|
|
97
97
|
user_mapping = get_user_mapping
|
98
98
|
group_mapping = get_group_mapping
|
99
99
|
|
100
|
-
cmd = "docker run -p '#{@options['sync_host_port']}:873' -v #{volume_name}:#{@options['dest']} #{user_mapping} #{group_mapping} -e VOLUME=#{@options['dest']} --name #{container_name} -d #{@docker_image}"
|
100
|
+
cmd = "docker run -p '#{@options['sync_host_port']}:873' -v #{volume_name}:#{@options['dest']} #{user_mapping} #{group_mapping} -e VOLUME=#{@options['dest']} -e TZ=${TZ-`readlink /etc/localtime | sed -e 's,/usr/share/zoneinfo/,,'`} --name #{container_name} -d #{@docker_image}"
|
101
101
|
else # container already created, just start / reuse it
|
102
102
|
say_status 'ok', "starting #{container_name} container", :white if @options['verbose']
|
103
103
|
cmd = "docker start #{container_name}"
|
@@ -1,18 +1,16 @@
|
|
1
1
|
require 'thor/shell'
|
2
2
|
require 'docker-sync/preconditions'
|
3
3
|
require 'open3'
|
4
|
-
require 'socket'
|
5
4
|
require 'terminal-notifier'
|
6
5
|
|
7
6
|
module Docker_Sync
|
8
7
|
module SyncStrategy
|
9
|
-
class
|
8
|
+
class Unison_Onesided
|
10
9
|
include Thor::Shell
|
11
10
|
|
12
11
|
@options
|
13
12
|
@sync_name
|
14
13
|
@watch_thread
|
15
|
-
@local_server_pid
|
16
14
|
UNISON_CONTAINER_PORT = '5000'
|
17
15
|
def initialize(sync_name, options)
|
18
16
|
@sync_name = sync_name
|
@@ -21,7 +19,7 @@ module Docker_Sync
|
|
21
19
|
if @options.key?('image')
|
22
20
|
@docker_image = @options['image']
|
23
21
|
else
|
24
|
-
@docker_image = 'eugenmayer/unison:
|
22
|
+
@docker_image = 'eugenmayer/unison:onesided'
|
25
23
|
end
|
26
24
|
begin
|
27
25
|
Preconditions::unison_available
|
@@ -34,7 +32,6 @@ module Docker_Sync
|
|
34
32
|
|
35
33
|
def run
|
36
34
|
start_container
|
37
|
-
start_local_server
|
38
35
|
sync
|
39
36
|
end
|
40
37
|
|
@@ -68,16 +65,11 @@ module Docker_Sync
|
|
68
65
|
args.push(@options['src'])
|
69
66
|
args.push('-auto')
|
70
67
|
args.push('-batch')
|
71
|
-
args.push('-owner') if @options['sync_userid'] == 'from_host'
|
72
|
-
args.push('-numericids') if @options['sync_userid'] == 'from_host'
|
73
68
|
args.push(@options['sync_args']) if @options.key?('sync_args')
|
74
69
|
args.push("socket://#{@options['sync_host_ip']}:#{@options['sync_host_port']}/")
|
75
70
|
args.push('-debug all') if @options['verbose']
|
76
|
-
if @options.key?('sync_user') || @options.key?('sync_group') || @options.key?('sync_groupid')
|
77
|
-
raise('Unison does not support sync_user, sync_group, sync_groupid - please use rsync if you need that')
|
78
|
-
end
|
79
|
-
if @options.key?('sync_userid') && @options['sync_userid'] != 'from_host'
|
80
|
-
raise('Unison does not support sync_userid with a parameter different than \'from_host\'')
|
71
|
+
if @options.key?('sync_user') || @options.key?('sync_group') || @options.key?('sync_groupid') || @options.key?('sync_userid')
|
72
|
+
raise('Unison does not support sync_user, sync_group, sync_groupid or sync_userid - please use rsync if you need that')
|
81
73
|
end
|
82
74
|
return args
|
83
75
|
end
|
@@ -86,33 +78,13 @@ module Docker_Sync
|
|
86
78
|
say_status 'ok', 'Starting unison', :white
|
87
79
|
container_name = get_container_name
|
88
80
|
volume_name = get_volume_name
|
89
|
-
|
90
|
-
|
91
|
-
env['ENABLE_WATCH'] = 'true'
|
92
|
-
# Excludes for fswatch and unison uses the same value both in the host and in the container
|
93
|
-
env['FSWATCH_EXCLUDES'] = @options['watch_excludes'].map { |pattern| "--exclude='#{pattern}'" }.join(' ') if @options.key?('watch_excludes')
|
94
|
-
env['UNISON_EXCLUDES'] = @options['sync_excludes'].map { |pattern| "-ignore='Path #{pattern}'" }.join(' ') if @options.key?('sync_excludes')
|
95
|
-
# Specify the IP of the host to call the host unison server from the container
|
96
|
-
env['HOST_IP'] = get_host_ip
|
97
|
-
env['UNISON_HOST_PORT'] = @options['unison-dualside_host_server_port']
|
98
|
-
|
99
|
-
if @options['sync_userid'] == 'from_host'
|
100
|
-
env['UNISON_DIR_OWNER'] = Process.uid
|
101
|
-
end
|
102
|
-
|
103
|
-
additional_docker_env = env.map{ |key,value| "-e #{key}=\"#{value}\"" }.join(' ')
|
104
|
-
running = `docker ps --filter 'status=running' --filter 'name=#{container_name}' | grep #{container_name}`
|
81
|
+
running = `docker ps --filter 'status=running' --filter 'name=#{container_name}' | sed -E -n 's/.*\\s(.*)$/\\1/p' | grep '^#{container_name}$'`
|
105
82
|
if running == ''
|
106
83
|
say_status 'ok', "#{container_name} container not running", :white if @options['verbose']
|
107
|
-
exists = `docker ps --filter "status=exited" --filter "name=#{container_name}" | grep
|
84
|
+
exists = `docker ps --filter "status=exited" --filter "name=#{container_name}" | sed -E -n 's/.*\\s(.*)$/\\1/p' | grep '^#{container_name}$'`
|
108
85
|
if exists == ''
|
109
86
|
say_status 'ok', "creating #{container_name} container", :white if @options['verbose']
|
110
|
-
cmd = "docker run -p '#{@options['sync_host_port']}:#{UNISON_CONTAINER_PORT}'
|
111
|
-
-v #{volume_name}:#{@options['dest']} \
|
112
|
-
-e UNISON_DIR=#{@options['dest']} \
|
113
|
-
#{additional_docker_env} \
|
114
|
-
--name #{container_name} \
|
115
|
-
-d #{@docker_image}"
|
87
|
+
cmd = "docker run -p '#{@options['sync_host_port']}:#{UNISON_CONTAINER_PORT}' -v #{volume_name}:#{@options['dest']} -e UNISON_DIR=#{@options['dest']} -e TZ=${TZ-`readlink /etc/localtime | sed -e 's,/usr/share/zoneinfo/,,'`} --name #{container_name} -d #{@docker_image}"
|
116
88
|
else
|
117
89
|
say_status 'ok', "starting #{container_name} container", :white if @options['verbose']
|
118
90
|
cmd = "docker start #{container_name}"
|
@@ -124,48 +96,11 @@ module Docker_Sync
|
|
124
96
|
end
|
125
97
|
say_status 'ok', "starting initial sync of #{container_name}", :white if @options['verbose']
|
126
98
|
# this sleep is needed since the container could be not started
|
127
|
-
sleep
|
99
|
+
sleep 1
|
128
100
|
sync
|
129
101
|
say_status 'success', 'Unison server started', :green
|
130
102
|
end
|
131
103
|
|
132
|
-
# Start an unison server on the host
|
133
|
-
def start_local_server
|
134
|
-
say_status 'ok', 'Starting local unison server', :white
|
135
|
-
|
136
|
-
if not @options.key?('unison-dualside_host_server_port')
|
137
|
-
raise('Missing unison-dualside_host_server_port option. In order to get files synced back automatically from the
|
138
|
-
container, you must specify the port of the local unison server.')
|
139
|
-
end
|
140
|
-
cmd = "unison -socket #{@options['unison-dualside_host_server_port']}"
|
141
|
-
say_status 'command', cmd, :white if @options['verbose']
|
142
|
-
|
143
|
-
# Store the pid of the local unison server, to kill it when docker-sync stops
|
144
|
-
@local_server_pid = Process.fork do
|
145
|
-
# Run the local unison server in the source folder
|
146
|
-
Dir.chdir(@options['src']){
|
147
|
-
`#{cmd}` || raise('Start of local unison server failed')
|
148
|
-
}
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# Try to guess the IP of the host to call the unison server from the container
|
153
|
-
# Or use the value specified in the config option
|
154
|
-
def get_host_ip
|
155
|
-
if @options['unison-dualside_host_ip'] != 'auto' && @options.key?('unison-dualside_host_ip')
|
156
|
-
host_ip = @options['host_ip']
|
157
|
-
elsif @options['unison-dualside_host_ip'] == 'auto' || (not @options.key?('unison-dualside_host_ip'))
|
158
|
-
host_ip = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address
|
159
|
-
end
|
160
|
-
return host_ip
|
161
|
-
end
|
162
|
-
|
163
|
-
# Kill the local unison server
|
164
|
-
def stop_local_server
|
165
|
-
Process.kill "TERM", @local_server_pid
|
166
|
-
Process.wait @local_server_pid
|
167
|
-
end
|
168
|
-
|
169
104
|
def get_container_name
|
170
105
|
return "#{@sync_name}"
|
171
106
|
end
|
@@ -192,7 +127,6 @@ module Docker_Sync
|
|
192
127
|
say_status 'ok', "Stopping sync container #{get_container_name}"
|
193
128
|
begin
|
194
129
|
stop_container
|
195
|
-
stop_local_server
|
196
130
|
rescue Exception => e
|
197
131
|
say_status 'error', "Stopping failed of #{get_container_name}:", :red
|
198
132
|
puts e.message
|
@@ -1,28 +1,33 @@
|
|
1
1
|
require 'thor/shell'
|
2
2
|
require 'docker-sync/preconditions'
|
3
|
+
require 'docker-sync/execution'
|
3
4
|
require 'open3'
|
5
|
+
require 'socket'
|
4
6
|
require 'terminal-notifier'
|
5
7
|
|
6
8
|
module Docker_Sync
|
7
9
|
module SyncStrategy
|
8
10
|
class Unison
|
9
11
|
include Thor::Shell
|
12
|
+
include Execution
|
10
13
|
|
11
14
|
@options
|
12
15
|
@sync_name
|
13
16
|
@watch_thread
|
17
|
+
@local_server_pid
|
14
18
|
UNISON_CONTAINER_PORT = '5000'
|
15
19
|
def initialize(sync_name, options)
|
16
|
-
@sync_name = sync_name
|
17
20
|
@options = options
|
21
|
+
@sync_name = sync_name
|
18
22
|
# if a custom image is set, apply it
|
19
23
|
if @options.key?('image')
|
20
24
|
@docker_image = @options['image']
|
21
25
|
else
|
22
|
-
@docker_image = 'eugenmayer/unison'
|
26
|
+
@docker_image = 'eugenmayer/unison:unox'
|
23
27
|
end
|
24
28
|
begin
|
25
29
|
Preconditions::unison_available
|
30
|
+
Preconditions::unox_available
|
26
31
|
rescue Exception => e
|
27
32
|
say_status 'error', "#{@sync_name} has been configured to sync with unison, but no unison available", :red
|
28
33
|
say_status 'error', e.message, :red
|
@@ -31,10 +36,41 @@ module Docker_Sync
|
|
31
36
|
end
|
32
37
|
|
33
38
|
def run
|
39
|
+
increase_watcher_limit if @options.key?('max_inotify_watches')
|
34
40
|
start_container
|
35
41
|
sync
|
36
42
|
end
|
37
43
|
|
44
|
+
def increase_watcher_limit
|
45
|
+
current_max_files_per_proc = `sysctl kern.maxfilesperproc | awk '{print $2}'`
|
46
|
+
if current_max_files_per_proc.to_f < @options['max_inotify_watches']
|
47
|
+
cmd = 'sudo sysctl -w kern.maxfilesperproc=' + @options['max_inotify_watches'].to_s
|
48
|
+
say_status 'command', cmd, :white
|
49
|
+
`#{cmd}` || raise('Unable to increase maxfilesperproc')
|
50
|
+
else
|
51
|
+
say_status 'command', 'Current maxfilesperproc set to ' + current_max_files_per_proc.to_s, :white
|
52
|
+
end
|
53
|
+
current_max_files = `sysctl kern.maxfiles | awk '{print $2}'`
|
54
|
+
if current_max_files.to_f < @options['max_inotify_watches']
|
55
|
+
cmd = 'sudo sysctl -w kern.maxfiles=' + @options['max_inotify_watches'].to_s
|
56
|
+
say_status 'command', cmd, :white
|
57
|
+
`#{cmd}` || raise('Unable to increase maxfiles')
|
58
|
+
else
|
59
|
+
say_status 'command', 'Current maxfiles set to ' + current_max_files.to_s, :white
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def watch
|
64
|
+
args = sync_options
|
65
|
+
args.push("-repeat watch")
|
66
|
+
cmd = ''
|
67
|
+
cmd = cmd + 'ulimit -n ' + @options['max_inotify_watches'].to_s + ' && ' if @options.key?('max_inotify_watches')
|
68
|
+
cmd = cmd + 'unison ' + args.join(' ')
|
69
|
+
|
70
|
+
say_status 'command', cmd, :white if @options['verbose']
|
71
|
+
forkexec(cmd, "Sync #{@sync_name}", :blue)
|
72
|
+
end
|
73
|
+
|
38
74
|
def sync
|
39
75
|
args = sync_options
|
40
76
|
cmd = 'unison ' + args.join(' ')
|
@@ -47,7 +83,7 @@ module Docker_Sync
|
|
47
83
|
say_status 'message', stderr
|
48
84
|
else
|
49
85
|
TerminalNotifier.notify(
|
50
|
-
|
86
|
+
"Synced #{@options['src']}", :title => @sync_name
|
51
87
|
) if @options['notify_terminal']
|
52
88
|
say_status 'ok', "Synced #{@options['src']}", :white
|
53
89
|
if @options['verbose']
|
@@ -66,10 +102,11 @@ module Docker_Sync
|
|
66
102
|
args.push('-auto')
|
67
103
|
args.push('-batch')
|
68
104
|
args.push(@options['sync_args']) if @options.key?('sync_args')
|
69
|
-
|
70
|
-
args.push(
|
71
|
-
|
72
|
-
|
105
|
+
sync_host_port = get_host_port(get_container_name, UNISON_CONTAINER_PORT)
|
106
|
+
args.push("socket://#{@options['sync_host_ip']}:#{sync_host_port}")
|
107
|
+
|
108
|
+
if @options.key?('sync_group') || @options.key?('sync_groupid')
|
109
|
+
raise('Unison does not support sync_user, sync_group, sync_groupid - please use rsync if you need that')
|
73
110
|
end
|
74
111
|
return args
|
75
112
|
end
|
@@ -78,30 +115,62 @@ module Docker_Sync
|
|
78
115
|
say_status 'ok', 'Starting unison', :white
|
79
116
|
container_name = get_container_name
|
80
117
|
volume_name = get_volume_name
|
118
|
+
env = {}
|
119
|
+
|
120
|
+
env['UNISON_EXCLUDES'] = @options['sync_excludes'].map { |pattern| "-ignore='Path #{pattern}'" }.join(' ') if @options.key?('sync_excludes')
|
121
|
+
env['UNISON_OWNER'] = @options['sync_user'] if @options.key?('sync_user')
|
122
|
+
env['MAX_INOTIFY_WATCHES'] = @options['max_inotify_watches'] if @options.key?('max_inotify_watches')
|
123
|
+
if @options['sync_userid'] == 'from_host'
|
124
|
+
env['UNISON_OWNER_UID'] = Process.uid
|
125
|
+
else
|
126
|
+
env['UNISON_OWNER_UID'] = @options['sync_userid'] if @options.key?('sync_userid')
|
127
|
+
end
|
81
128
|
|
82
|
-
|
129
|
+
additional_docker_env = env.map{ |key,value| "-e #{key}=\"#{value}\"" }.join(' ')
|
130
|
+
running = `docker ps --filter 'status=running' --filter 'name=#{container_name}' | sed -E -n 's/.*\\s(.*)$/\\1/p' | grep '^#{container_name}$'`
|
83
131
|
if running == ''
|
84
132
|
say_status 'ok', "#{container_name} container not running", :white if @options['verbose']
|
85
|
-
exists = `docker ps --filter "status=exited" --filter "name=#{container_name}" | grep
|
133
|
+
exists = `docker ps --filter "status=exited" --filter "name=#{container_name}" | sed -E -n 's/.*\\s(.*)$/\\1/p' | grep '^#{container_name}$'`
|
86
134
|
if exists == ''
|
87
135
|
say_status 'ok', "creating #{container_name} container", :white if @options['verbose']
|
88
|
-
|
136
|
+
run_privileged = '--privileged' if @options.key?('max_inotify_watches') #TODO: replace by the minimum capabilities required
|
137
|
+
cmd = "docker run -p '127.0.0.1::#{UNISON_CONTAINER_PORT}' \
|
138
|
+
-v #{volume_name}:#{@options['dest']} \
|
139
|
+
-e UNISON_DIR=#{@options['dest']} \
|
140
|
+
-e TZ=${TZ-`readlink /etc/localtime | sed -e 's,/usr/share/zoneinfo/,,'`} \
|
141
|
+
#{additional_docker_env} \
|
142
|
+
#{run_privileged} \
|
143
|
+
--name #{container_name} \
|
144
|
+
-d #{@docker_image}"
|
89
145
|
else
|
90
146
|
say_status 'ok', "starting #{container_name} container", :white if @options['verbose']
|
91
|
-
cmd = "docker start #{container_name}"
|
147
|
+
cmd = "docker start #{container_name} && docker exec #{container_name} supervisorctl restart unison"
|
92
148
|
end
|
93
|
-
say_status 'command', cmd, :white if @options['verbose']
|
94
|
-
`#{cmd}` || raise('Start failed')
|
95
149
|
else
|
96
|
-
say_status 'ok', "#{container_name} container still running", :blue
|
150
|
+
say_status 'ok', "#{container_name} container still running, restarting unison in container", :blue
|
151
|
+
cmd = "docker exec #{container_name} supervisorctl restart unison"
|
97
152
|
end
|
153
|
+
say_status 'command', cmd, :white if @options['verbose']
|
154
|
+
`#{cmd}` || raise('Start failed')
|
98
155
|
say_status 'ok', "starting initial sync of #{container_name}", :white if @options['verbose']
|
99
156
|
# this sleep is needed since the container could be not started
|
100
|
-
sleep
|
157
|
+
sleep 5 # TODO: replace with unison -testserver
|
101
158
|
sync
|
102
159
|
say_status 'success', 'Unison server started', :green
|
103
160
|
end
|
104
161
|
|
162
|
+
def get_host_port(container_name, container_port)
|
163
|
+
cmd = 'docker inspect --format=" {{ .NetworkSettings.Ports }} " ' + container_name + ' | sed "s/.*map\[' + container_port + '[^ ]\+\s\([0-9]\+\).*/\1/"'
|
164
|
+
say_status 'command', cmd, :white if @options['verbose']
|
165
|
+
stdout, stderr, exit_status = Open3.capture3(cmd)
|
166
|
+
if not exit_status.success?
|
167
|
+
say_status 'command', cmd
|
168
|
+
say_status 'error', "Error getting mapped port, exit code #{$?.exitstatus}", :red
|
169
|
+
say_status 'message', stderr
|
170
|
+
end
|
171
|
+
return stdout.gsub("\n",'')
|
172
|
+
end
|
173
|
+
|
105
174
|
def get_container_name
|
106
175
|
return "#{@sync_name}"
|
107
176
|
end
|
@@ -18,6 +18,8 @@ class UpdateChecker
|
|
18
18
|
return
|
19
19
|
end
|
20
20
|
check_rsync_image unless DockerSyncConfig::is_first_run # do not check the image if its the first run - since this it will be downloaded anyway
|
21
|
+
check_unison_onesided_image unless DockerSyncConfig::is_first_run
|
22
|
+
check_unison_image unless DockerSyncConfig::is_first_run
|
21
23
|
check_and_warn(@config['update_enforce'])
|
22
24
|
end
|
23
25
|
|
@@ -44,6 +46,30 @@ class UpdateChecker
|
|
44
46
|
say_status 'success','Image is (now) up to date'
|
45
47
|
end
|
46
48
|
|
49
|
+
def check_unison_image
|
50
|
+
say_status 'ok','Checking if a newer unison image is available'
|
51
|
+
|
52
|
+
if system("docker pull eugenmayer/unison:unox | grep 'Downloaded newer image for'")
|
53
|
+
say_status 'warning', 'Downloaded newer image for unison', :red
|
54
|
+
say_status 'warning', 'Please use "docker-sync clean" before you start docker-sync again', :red
|
55
|
+
|
56
|
+
exit 0
|
57
|
+
end
|
58
|
+
say_status 'success','Image is (now) up to date'
|
59
|
+
end
|
60
|
+
|
61
|
+
def check_unison_onesided_image
|
62
|
+
say_status 'ok','Checking if a newer unison:onesided image is available'
|
63
|
+
|
64
|
+
if system("docker pull eugenmayer/unison:onesided | grep 'Downloaded newer image for'")
|
65
|
+
say_status 'warning', 'Downloaded newer image for unison:onesided', :red
|
66
|
+
say_status 'warning', 'Please use "docker-sync clean" before you start docker-sync again', :red
|
67
|
+
|
68
|
+
exit 0
|
69
|
+
end
|
70
|
+
say_status 'success','Image is (now) up to date'
|
71
|
+
end
|
72
|
+
|
47
73
|
def get_current_version
|
48
74
|
path = File.expand_path('../../../', __FILE__)
|
49
75
|
return File.read("#{path}/VERSION")
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'gem_update_checker'
|
2
|
+
require 'thor/actions'
|
3
|
+
require 'docker-sync/config'
|
4
|
+
|
5
|
+
class UpgradeChecker
|
6
|
+
include Thor::Shell
|
7
|
+
@config
|
8
|
+
def initialize
|
9
|
+
@config = DockerSyncConfig::global_config
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
unless should_run
|
14
|
+
return
|
15
|
+
end
|
16
|
+
check_and_warn
|
17
|
+
end
|
18
|
+
|
19
|
+
def last_upgraded_version
|
20
|
+
@config['upgrade_status'] || ''
|
21
|
+
end
|
22
|
+
|
23
|
+
def should_run
|
24
|
+
# get the update_status which is the version of the update hook which has been run already
|
25
|
+
upgrade_status = last_upgraded_version
|
26
|
+
if upgrade_status == '' || Gem::Version.new(upgrade_status) < Gem::Version.new(get_current_version) # thats how we compare the version
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def get_current_version
|
35
|
+
path = File.expand_path('../../../', __FILE__)
|
36
|
+
return File.read("#{path}/VERSION")
|
37
|
+
end
|
38
|
+
|
39
|
+
def docker_sync_update_check
|
40
|
+
gem_name = 'docker-sync'
|
41
|
+
current_version = get_current_version
|
42
|
+
checker = GemUpdateChecker::Client.new(gem_name, current_version)
|
43
|
+
return checker
|
44
|
+
end
|
45
|
+
|
46
|
+
def check_and_warn
|
47
|
+
# this is the upgrade hook for the unison-unox introduction / rename of unison
|
48
|
+
if Gem::Version.new(last_upgraded_version) < Gem::Version.new('0.1.0')
|
49
|
+
Thor::Shell::Basic.new.say_status 'warning', 'Please be aware that with the strategy "unison" is now called unison-onesided and you might need to migrate. See https://github.com/EugenMayer/docker-sync/wiki/Migration-Guide for more informations', :red
|
50
|
+
unless Thor::Shell::Basic.new.yes?('Shall we continue?')
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# update the upgrade_status
|
56
|
+
@config['upgrade_status'] = "#{get_current_version}"
|
57
|
+
DockerSyncConfig::global_config_save(@config)
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'thor/shell'
|
2
|
+
require 'docker-sync/execution'
|
3
|
+
require 'docker-sync/preconditions'
|
4
|
+
require 'docker-sync/sync_strategy/unison'
|
5
|
+
|
6
|
+
module Docker_Sync
|
7
|
+
module WatchStrategy
|
8
|
+
class Unison
|
9
|
+
include Execution
|
10
|
+
|
11
|
+
@options
|
12
|
+
@sync_name
|
13
|
+
@watch_fork
|
14
|
+
|
15
|
+
def initialize(sync_name, options)
|
16
|
+
@options = options
|
17
|
+
@sync_name = sync_name
|
18
|
+
@watch_fork = nil
|
19
|
+
# instantiate the sync task to easily access all common parameters between
|
20
|
+
# unison sync and watch
|
21
|
+
# basically unison watch is the command with the additionnal -repeat watch option
|
22
|
+
# note: this doesn't run a sync
|
23
|
+
@unison = Docker_Sync::SyncStrategy::Unison.new(@sync_name, @options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
@watch_fork = @unison.watch
|
28
|
+
end
|
29
|
+
|
30
|
+
def stop
|
31
|
+
Process.kill "TERM", @watch_fork
|
32
|
+
Process.wait @watch_fork
|
33
|
+
end
|
34
|
+
|
35
|
+
def clean
|
36
|
+
end
|
37
|
+
|
38
|
+
def watch
|
39
|
+
end
|
40
|
+
|
41
|
+
def watch_options
|
42
|
+
end
|
43
|
+
|
44
|
+
def watch_fork
|
45
|
+
return @watch_fork
|
46
|
+
end
|
47
|
+
|
48
|
+
def watch_thread
|
49
|
+
return nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/tasks/stack/stack.thor
CHANGED
@@ -2,6 +2,7 @@ require 'docker-sync/sync_manager'
|
|
2
2
|
require 'docker-sync/config'
|
3
3
|
require 'docker-sync/preconditions'
|
4
4
|
require 'docker-sync/update_check'
|
5
|
+
require 'docker-sync/upgrade_check'
|
5
6
|
require 'docker/compose'
|
6
7
|
require 'docker-sync/compose'
|
7
8
|
class Stack < Thor
|
@@ -15,6 +16,9 @@ class Stack < Thor
|
|
15
16
|
updates = UpdateChecker.new
|
16
17
|
updates.run
|
17
18
|
|
19
|
+
upgrades = UpgradeChecker.new
|
20
|
+
upgrades.run
|
21
|
+
|
18
22
|
begin
|
19
23
|
Preconditions::check_all_preconditions
|
20
24
|
rescue Exception => e
|
@@ -33,7 +37,7 @@ class Stack < Thor
|
|
33
37
|
end
|
34
38
|
end
|
35
39
|
|
36
|
-
@sync_manager =
|
40
|
+
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
37
41
|
@sync_manager.run(options[:sync_name])
|
38
42
|
global_options = @sync_manager.global_options
|
39
43
|
@compose_manager = ComposeManager.new(global_options)
|
@@ -76,7 +80,7 @@ class Stack < Thor
|
|
76
80
|
return
|
77
81
|
end
|
78
82
|
end
|
79
|
-
@sync_manager =
|
83
|
+
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
80
84
|
global_options = @sync_manager.global_options
|
81
85
|
# shutdown compose first
|
82
86
|
@compose_manager = ComposeManager.new(global_options)
|
data/tasks/sync/sync.thor
CHANGED
@@ -2,6 +2,7 @@ require 'docker-sync/sync_manager'
|
|
2
2
|
require 'docker-sync/config'
|
3
3
|
require 'docker-sync/preconditions'
|
4
4
|
require 'docker-sync/update_check'
|
5
|
+
require 'docker-sync/upgrade_check'
|
5
6
|
|
6
7
|
class Sync < Thor
|
7
8
|
|
@@ -13,7 +14,8 @@ class Sync < Thor
|
|
13
14
|
# do run update check in the start command only
|
14
15
|
updates = UpdateChecker.new
|
15
16
|
updates.run
|
16
|
-
|
17
|
+
upgrades = UpgradeChecker.new
|
18
|
+
upgrades.run
|
17
19
|
begin
|
18
20
|
Preconditions::check_all_preconditions
|
19
21
|
rescue Exception => e
|
@@ -31,7 +33,7 @@ class Sync < Thor
|
|
31
33
|
return
|
32
34
|
end
|
33
35
|
end
|
34
|
-
@sync_manager =
|
36
|
+
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
35
37
|
@sync_manager.run(options[:sync_name])
|
36
38
|
@sync_manager.join_threads
|
37
39
|
end
|
@@ -55,7 +57,7 @@ class Sync < Thor
|
|
55
57
|
return
|
56
58
|
end
|
57
59
|
end
|
58
|
-
@sync_manager =
|
60
|
+
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
59
61
|
@sync_manager.sync(options[:sync_name])
|
60
62
|
end
|
61
63
|
|
@@ -78,7 +80,7 @@ class Sync < Thor
|
|
78
80
|
return
|
79
81
|
end
|
80
82
|
end
|
81
|
-
@sync_manager =
|
83
|
+
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
82
84
|
@sync_manager.clean(options[:sync_name])
|
83
85
|
say_status 'success', 'Finished cleanup. Removed stopped, removed sync container and removed there volumes', :green
|
84
86
|
end
|
@@ -105,12 +107,18 @@ class Sync < Thor
|
|
105
107
|
end
|
106
108
|
|
107
109
|
say_status 'ok',"Found configuration at #{config_path}"
|
108
|
-
@sync_manager =
|
110
|
+
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
109
111
|
@sync_manager.get_sync_points.each do |name, config|
|
110
112
|
say_status name, "On address #{config['sync_host_ip']}:#{config['sync_host_port']}",:white unless options['verbose']
|
111
113
|
puts "\n---------------[#{name}] #{config['sync_host_ip']}:#{config['sync_host_port']} ---------------\n" if options['verbose']
|
112
114
|
print_table(config) if options['verbose']
|
113
115
|
end
|
114
116
|
end
|
117
|
+
desc 'start', 'Start all sync configurations in this project'
|
118
|
+
def upgrade
|
119
|
+
# do run update check in the start command only
|
120
|
+
upgrades = UpgradeChecker.new
|
121
|
+
upgrades.run
|
122
|
+
end
|
115
123
|
|
116
124
|
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.1.0
|
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-
|
11
|
+
date: 2016-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -98,11 +98,13 @@ files:
|
|
98
98
|
- lib/docker-sync/sync_manager.rb
|
99
99
|
- lib/docker-sync/sync_process.rb
|
100
100
|
- lib/docker-sync/sync_strategy/rsync.rb
|
101
|
-
- lib/docker-sync/sync_strategy/unison-
|
101
|
+
- lib/docker-sync/sync_strategy/unison-onesided.rb
|
102
102
|
- lib/docker-sync/sync_strategy/unison.rb
|
103
103
|
- lib/docker-sync/update_check.rb
|
104
|
+
- lib/docker-sync/upgrade_check.rb
|
104
105
|
- lib/docker-sync/watch_strategy/dummy.rb
|
105
106
|
- lib/docker-sync/watch_strategy/fswatch.rb
|
107
|
+
- lib/docker-sync/watch_strategy/unison.rb
|
106
108
|
- tasks/stack/stack.thor
|
107
109
|
- tasks/sync/sync.thor
|
108
110
|
homepage: https://github.com/EugenMayer/docker_sync
|
@@ -117,7 +119,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
117
119
|
requirements:
|
118
120
|
- - ">="
|
119
121
|
- !ruby/object:Gem::Version
|
120
|
-
version: '0'
|
122
|
+
version: '2.0'
|
121
123
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
124
|
requirements:
|
123
125
|
- - ">="
|