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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f91f3a2eb84005eb9ecd865282ace48c86f2f2c4
4
- data.tar.gz: 3a5ca77ceab1cbe91ae630c6cd7c102db467f0c2
3
+ metadata.gz: 6cb5e3a077d9de57cad162afbdab55c0aa2ed36f
4
+ data.tar.gz: 73b3b75811e54265c180c4e6cc540a23f762a394
5
5
  SHA512:
6
- metadata.gz: 35de54d7b90e0dd76617a8ad102cea41ff8c837904790f96313c8943eb5b9c7e13249bfa3209673788fec538eb9784f173393dddfeff6f81e8a967be10feceda
7
- data.tar.gz: 30776d4c9c53279ffa349ac44f50efb9b204a7577678d67c44baaeef5131f94eab02e62d28ae423dbd641e3a62f7ae5fe7d0f9b8f86bf8a6bc38ba799adac901
6
+ metadata.gz: 420ff17cc42f4d5422e85604f5e28b1ff10e6a820435130bca44dccf6f970c349583d068195ca5f460a9c89b230ad94f255f56dac03f9b79b95036c54cb60eed
7
+ data.tar.gz: 0d1284902dcb5b72fe1cde3c28f9279efba62db2990f2c9c9837e3461b8f0032f2c61b6b2f1b588ba4fc6936cf220b195be25b3c3221d17d7e952674a77e6d61
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.15
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
- end
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 Docker_Rsync
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 sync_host_port].each do |key|
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
- sync_process.watch_thread.join unless sync_process.watch_thread.nil?
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
- @watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
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 #{container_name}`
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 #{container_name}`
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 Unison_DualSide
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:dualside'
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
- env = {}
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 #{container_name}`
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 5 # TODO: replace with unison -testserver
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
- "Synced #{@options['src']}", :title => @sync_name
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
- args.push("socket://#{@options['sync_host_ip']}:#{@options['sync_host_port']}/")
70
- args.push('-debug all') if @options['verbose']
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')
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
- running = `docker ps --filter 'status=running' --filter 'name=#{container_name}' | grep #{container_name}`
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 #{container_name}`
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
- cmd = "docker run -p '#{@options['sync_host_port']}:#{UNISON_CONTAINER_PORT}' -v #{volume_name}:#{@options['dest']} -e UNISON_DIR=#{@options['dest']} --name #{container_name} -d #{@docker_image}"
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 1
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
@@ -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 = Docker_Rsync::SyncManager.new(:config_path => config_path)
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 = Docker_Rsync::SyncManager.new(:config_path => config_path)
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)
@@ -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 = Docker_Rsync::SyncManager.new(:config_path => config_path)
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 = Docker_Rsync::SyncManager.new(:config_path => config_path)
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 = Docker_Rsync::SyncManager.new(:config_path => config_path)
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 = Docker_Rsync::SyncManager.new(:config_path => config_path)
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.15
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-07-25 00:00:00.000000000 Z
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-dualside.rb
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
  - - ">="