docker-sync 0.0.15 → 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 +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
|
- - ">="
|