docker-sync 0.1.5 → 0.2.0.beta5
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/bin/docker-sync-daemon +18 -0
- data/bin/docker-sync-stack +0 -11
- data/lib/docker-sync/compose.rb +29 -12
- data/lib/docker-sync/config.rb +29 -3
- data/lib/docker-sync/config_template.rb +14 -0
- data/lib/docker-sync/preconditions.rb +42 -9
- data/lib/docker-sync/sync_manager.rb +24 -6
- data/lib/docker-sync/sync_process.rb +10 -13
- data/lib/docker-sync/sync_strategy/rsync.rb +1 -1
- data/lib/docker-sync/sync_strategy/unison.rb +25 -5
- data/lib/docker-sync/update_check.rb +2 -14
- data/lib/docker-sync/upgrade_check.rb +13 -5
- data/lib/docker-sync/watch_strategy/fswatch.rb +1 -1
- data/lib/docker-sync/watch_strategy/unison.rb +1 -1
- data/tasks/daemon/daemon.thor +50 -0
- data/tasks/stack/stack.thor +13 -0
- data/tasks/sync/sync.thor +152 -65
- metadata +48 -5
- data/lib/docker-sync/sync_strategy/unison-onesided.rb +0 -137
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94abd2200815b1114719e58149b90ecf713ff5ee
|
4
|
+
data.tar.gz: f3e8d3f36eaf2046aa0f664670659aba57265e19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc26e58c0a5641cd7aadb5fd6af54a07cfb7b6e3760d0f71782aae7d83273ceadfd4cbc6b8f58e396aaeda64d53f8d951c30c02a172d610fb1a9178425b3aabc
|
7
|
+
data.tar.gz: bad573788249515c63475299b2058b38240589ae27fce69e1ce9396729d22a6db5ea4da957dbee6932a08aae2c22b2572af088e464fb09538be8b479a7f1ec51
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0.beta5
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
3
|
+
file = File.expand_path(File.dirname(file))
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../lib', file)
|
5
|
+
|
6
|
+
require 'thor'
|
7
|
+
require 'docker-sync/update_check'
|
8
|
+
|
9
|
+
thor = File.expand_path('../tasks/daemon', file)
|
10
|
+
Dir.glob(File.join(thor, '/**/*.thor')).each { |taskfile|
|
11
|
+
load taskfile
|
12
|
+
}
|
13
|
+
thor = File.expand_path('../tasks/sync', file)
|
14
|
+
Dir.glob(File.join(thor, '/**/*.thor')).each { |taskfile|
|
15
|
+
load taskfile
|
16
|
+
}
|
17
|
+
|
18
|
+
Daemon.start
|
data/bin/docker-sync-stack
CHANGED
@@ -3,20 +3,9 @@ file = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
|
3
3
|
file = File.expand_path(File.dirname(file))
|
4
4
|
$LOAD_PATH.unshift File.expand_path('../lib', file)
|
5
5
|
|
6
|
-
|
7
6
|
require 'thor'
|
8
7
|
require 'docker-sync/update_check'
|
9
8
|
|
10
|
-
# TODO: remove this later, not longer needed
|
11
|
-
#begin
|
12
|
-
# gem 'docker-compose'
|
13
|
-
#rescue Gem::LoadError
|
14
|
-
# puts "docker-compose gem is missing, installing 'gem install docker-compose'"
|
15
|
-
# system('gem install docker-compose')
|
16
|
-
# puts 'docker-compose ruby gem installed, please start docker-sync-stack again'
|
17
|
-
# exit 0
|
18
|
-
#end
|
19
|
-
|
20
9
|
thor = File.expand_path('../tasks/stack', file)
|
21
10
|
Dir.glob(File.join(thor, '/**/*.thor')).each { |taskfile|
|
22
11
|
# noinspection RubyResolve
|
data/lib/docker-sync/compose.rb
CHANGED
@@ -10,22 +10,15 @@ class ComposeManager
|
|
10
10
|
### production docker-compose.yml
|
11
11
|
compose_files = [File.expand_path('docker-compose.yml')]
|
12
12
|
if @global_options.key?('compose-file-path')
|
13
|
-
|
14
|
-
|
15
|
-
raise("Your referenced docker-compose file in docker-sync.yml was not found at #{@global_options['compose-file-path']}")
|
16
|
-
end
|
17
|
-
compose_files = [path] # replace
|
13
|
+
compose_files = [] # replace
|
14
|
+
apply_path_settings(compose_files, @global_options['compose-file-path'], 'compose-file-path')
|
18
15
|
end
|
19
16
|
|
20
17
|
### development docker-compose-dev.yml
|
21
18
|
if @global_options.key?('compose-dev-file-path')
|
22
19
|
# explicit path given
|
23
|
-
|
24
|
-
unless File.exist?(path)
|
25
|
-
raise("Your referenced docker-compose-dev file in docker-sync.yml was not found at #{@global_options['compose-dev-file-path']}")
|
26
|
-
end
|
20
|
+
apply_path_settings(compose_files, @global_options['compose-dev-file-path'], 'compose-dev-file-path')
|
27
21
|
say_status 'ok',"Found explicit docker-compose-dev.yml and using it from #{@global_options['compose-dev-file-path']}", :green
|
28
|
-
compose_files.push path # add
|
29
22
|
else
|
30
23
|
# try to find docker-compose-dev.yml
|
31
24
|
e = compose_files.to_enum
|
@@ -44,9 +37,9 @@ class ComposeManager
|
|
44
37
|
say_status 'ok','starting compose',:green
|
45
38
|
options = Hash.new
|
46
39
|
if @global_options['compose-force-build']
|
47
|
-
options[
|
40
|
+
options[:build] = true
|
48
41
|
end
|
49
|
-
@compose_session.up(options)
|
42
|
+
@compose_session.up(**options)
|
50
43
|
end
|
51
44
|
|
52
45
|
def stop
|
@@ -56,4 +49,28 @@ class ComposeManager
|
|
56
49
|
def clean
|
57
50
|
@compose_session.down
|
58
51
|
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
def apply_path_settings(compose_files, settings, error_key)
|
55
|
+
if settings.kind_of?(Array)
|
56
|
+
apply_filepaths(compose_files, settings, error_key)
|
57
|
+
else
|
58
|
+
apply_filepath(compose_files, settings, error_key)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def apply_filepath(array, filepath, error_key)
|
64
|
+
path = File.expand_path(filepath)
|
65
|
+
unless File.exist?(path)
|
66
|
+
raise("Your referenced docker-compose file in docker-sync.yml was not found at #{error_key}")
|
67
|
+
end
|
68
|
+
array.push path
|
69
|
+
end
|
70
|
+
|
71
|
+
def apply_filepaths(array, filepath_array, error_key)
|
72
|
+
filepath_array.each do |filepath|
|
73
|
+
apply_filepath(array, filepath, error_key)
|
74
|
+
end
|
75
|
+
end
|
59
76
|
end
|
data/lib/docker-sync/config.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
require 'pp'
|
2
2
|
require 'pathname'
|
3
3
|
require 'yaml'
|
4
|
-
|
4
|
+
require 'dotenv'
|
5
|
+
require 'docker-sync/config_template'
|
5
6
|
|
7
|
+
# this has basically completely reused from Thor::runner.rb - thank you!
|
6
8
|
module DockerSyncConfig
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
Dotenv.load
|
12
|
+
end
|
13
|
+
|
7
14
|
def self.global_config_location
|
8
15
|
return File.expand_path('~/.docker-sync-global.yml')
|
9
16
|
end
|
@@ -19,7 +26,7 @@ module DockerSyncConfig
|
|
19
26
|
# noinspection RubyStringKeysInHashInspection
|
20
27
|
defaults = {'update_check'=>true, 'update_last_check' => date.iso8601(9), 'update_enforce' => true}
|
21
28
|
if File.exist?(global_config_path)
|
22
|
-
config =
|
29
|
+
config = ConfigTemplate::interpolate_config_file(global_config_path)
|
23
30
|
config = defaults.merge(config)
|
24
31
|
return config
|
25
32
|
else
|
@@ -32,13 +39,32 @@ module DockerSyncConfig
|
|
32
39
|
File.open(global_config_path, 'w') {|f| f.write config.to_yaml }
|
33
40
|
end
|
34
41
|
|
42
|
+
|
43
|
+
def self.project_required_config_version
|
44
|
+
return '2'
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.project_ensure_configuration_version_compatibility(config)
|
48
|
+
return false unless config.key?('version')
|
49
|
+
return false if config['version'].to_s != project_required_config_version.to_s
|
50
|
+
return true
|
51
|
+
end
|
52
|
+
|
35
53
|
def self.project_config_path
|
36
54
|
files = project_config_find
|
37
55
|
if files.length > 0
|
38
|
-
|
56
|
+
path = files.pop
|
39
57
|
else
|
40
58
|
raise('No docker-sync.yml configuration found in your path ( traversing up ) Did you define it for your project?')
|
41
59
|
end
|
60
|
+
|
61
|
+
begin
|
62
|
+
config = YAML.load_file(path)
|
63
|
+
raise "Version of docker-sync.yml does not match the reqiured one" unless project_ensure_configuration_version_compatibility(config)
|
64
|
+
rescue Exception => e
|
65
|
+
raise "You docker-sync.yml file does either not include a version: \"#{project_required_config_version}\" setting or your setting (#{config['version']}) does not match the required version (#{project_required_config_version}). (Add this if you migrated from docker-sync 0.1.x)"
|
66
|
+
end
|
67
|
+
return path
|
42
68
|
end
|
43
69
|
|
44
70
|
# this has been ruthlessly stolen from Thor/runner.rb - please do not hunt me for that :)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require 'dotenv'
|
3
|
+
|
4
|
+
module ConfigTemplate
|
5
|
+
def self.interpolate_config_file(config_path)
|
6
|
+
env_hash = {}
|
7
|
+
ENV.each {|k,v| env_hash[k.to_sym] = v }
|
8
|
+
# assuming the checks that file exist have already been performed
|
9
|
+
config_string = File.read(config_path)
|
10
|
+
config_string.gsub!('${', '%{')
|
11
|
+
config_string = config_string % env_hash
|
12
|
+
return YAML.load(config_string)
|
13
|
+
end
|
14
|
+
end
|
@@ -4,7 +4,9 @@ module Preconditions
|
|
4
4
|
def self.check_all_preconditions
|
5
5
|
docker_available
|
6
6
|
docker_running
|
7
|
-
|
7
|
+
unison_available
|
8
|
+
unox_available
|
9
|
+
macfsevents_available
|
8
10
|
end
|
9
11
|
|
10
12
|
def self.docker_available
|
@@ -48,21 +50,52 @@ module Preconditions
|
|
48
50
|
if (find_executable0 'unison-fsmonitor').nil?
|
49
51
|
cmd1 = 'curl "https://raw.githubusercontent.com/hnsl/unox/master/unox.py" -o "/usr/local/bin/unison-fsmonitor" \
|
50
52
|
&& chmod +x /usr/local/bin/unison-fsmonitor'
|
51
|
-
|
52
|
-
Thor::Shell::Basic.new.say_status 'warning', 'Could not find unison-fsmonitor (for file watching) binary in $PATH.
|
53
|
-
if Thor::Shell::Basic.new.yes?('Shall I install unison-fsmonitor for you? ')
|
53
|
+
|
54
|
+
Thor::Shell::Basic.new.say_status 'warning', 'Could not find unison-fsmonitor (for file watching) binary in $PATH. We try to install unox now (for manual instracutions see https://github.com/hnsl/unox.)', :red
|
55
|
+
if Thor::Shell::Basic.new.yes?('Shall I install unison-fsmonitor for you? (y/N)')
|
54
56
|
system cmd1
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
57
|
+
else
|
58
|
+
raise("Please install it, see https://github.com/hnsl/unox, or simply run :\n #{cmd1} && #{cmd2}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.macfsevents_available
|
65
|
+
`python -c 'import fsevents'`
|
66
|
+
unless $?.success?
|
67
|
+
Thor::Shell::Basic.new.say_status 'warning','Could not find macfsevents. Will try to install it using pip', :red
|
68
|
+
sudo = false
|
69
|
+
if find_executable0('python') == '/usr/bin/python'
|
70
|
+
Thor::Shell::Basic.new.say_status 'ok','You seem to use the system python, we will need sudo below'
|
71
|
+
sudo = true
|
72
|
+
cmd2 = 'sudo easy_install pip && sudo pip install macfsevents'
|
73
|
+
else
|
74
|
+
Thor::Shell::Basic.new.say_status 'ok','You seem to have a custom python, using non-sudo commands'
|
75
|
+
sudo = false
|
76
|
+
cmd2 = 'easy_install pip && pip install macfsevents'
|
77
|
+
end
|
78
|
+
if sudo
|
79
|
+
question = 'I will ask you for you root password to install macfsevent by running (This will ask for sudo, since we use the system python)'
|
80
|
+
else
|
81
|
+
question = 'I will now install macfsevents for you by running'
|
82
|
+
end
|
83
|
+
|
84
|
+
Thor::Shell::Basic.new.say_status 'info', "#{question}: `#{cmd2}\n\n"
|
85
|
+
if Thor::Shell::Basic.new.yes?('Shall i continue? (y/N)')
|
59
86
|
system cmd2
|
60
87
|
if $?.exitstatus > 0
|
61
88
|
raise('Failed to install macfsevents, please file an issue with the output of the error')
|
62
89
|
end
|
90
|
+
`python -c 'import fsevents'`
|
91
|
+
unless $?.success?
|
92
|
+
raise('Somehow could not successfully install macfsevents even though i treed. Please report this issue')
|
93
|
+
end
|
63
94
|
else
|
64
|
-
raise(
|
95
|
+
raise('Please install macfsevents manually, see https://github.com/EugenMayer/docker-sync/wiki/1.-Installation')
|
65
96
|
end
|
66
97
|
end
|
98
|
+
|
99
|
+
|
67
100
|
end
|
68
101
|
end
|
@@ -4,6 +4,8 @@ require 'docker-sync/sync_process'
|
|
4
4
|
# noinspection RubyResolve
|
5
5
|
require 'docker-sync/execution'
|
6
6
|
require 'yaml'
|
7
|
+
require 'dotenv'
|
8
|
+
require 'docker-sync/config_template'
|
7
9
|
|
8
10
|
module Docker_sync
|
9
11
|
class SyncManager
|
@@ -14,6 +16,8 @@ module Docker_sync
|
|
14
16
|
@config_path
|
15
17
|
|
16
18
|
def initialize(options)
|
19
|
+
Dotenv.load
|
20
|
+
|
17
21
|
@sync_processes = []
|
18
22
|
@config_syncs = []
|
19
23
|
@config_global = []
|
@@ -26,7 +30,8 @@ module Docker_sync
|
|
26
30
|
raise "Config could not be loaded from #{@config_path} - it does not exist"
|
27
31
|
end
|
28
32
|
|
29
|
-
config =
|
33
|
+
config = ConfigTemplate::interpolate_config_file(@config_path)
|
34
|
+
|
30
35
|
validate_config(config)
|
31
36
|
@config_global = config['options'] || {}
|
32
37
|
@config_syncs = config['syncs']
|
@@ -44,9 +49,14 @@ module Docker_sync
|
|
44
49
|
def upgrade_syncs_config
|
45
50
|
@config_syncs.each do |name, config|
|
46
51
|
@config_syncs[name]['config_path'] = @config_path
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
|
53
|
+
# [nbr] convert the sync src from relative to absolute path
|
54
|
+
# preserve '/' as it may be significant to the sync cmd
|
55
|
+
absolute_path = File.expand_path(@config_syncs[name]['src'])
|
56
|
+
absolute_path << "/" if @config_syncs[name]['src'].end_with?("/")
|
57
|
+
@config_syncs[name]['src'] = absolute_path
|
58
|
+
|
59
|
+
@config_syncs[name]['cli_mode'] = @config_global['cli_mode'] || 'auto'
|
50
60
|
|
51
61
|
# set the global verbose setting, if the sync-endpoint does not define a own one
|
52
62
|
unless config.key?('verbose')
|
@@ -80,13 +90,14 @@ module Docker_sync
|
|
80
90
|
|
81
91
|
def validate_sync_config(name, sync_config)
|
82
92
|
config_mandatory = %w[src dest]
|
83
|
-
config_mandatory.push('sync_host_port')
|
93
|
+
config_mandatory.push('sync_host_port') if sync_config['sync_strategy'] == 'rsync' #TODO: Implement autodisovery for other strategies
|
84
94
|
config_mandatory.each do |key|
|
85
|
-
raise ("#{name} does not have #{key}
|
95
|
+
raise ("#{name} does not have #{key} configuration value set - this is mandatory") unless sync_config.key?(key)
|
86
96
|
end
|
87
97
|
end
|
88
98
|
|
89
99
|
def init_sync_processes(sync_name = nil)
|
100
|
+
return if @sync_processes.size != 0
|
90
101
|
if sync_name.nil?
|
91
102
|
@config_syncs.each { |name, sync_configuration|
|
92
103
|
@sync_processes.push(create_sync(name, sync_configuration))
|
@@ -114,6 +125,13 @@ module Docker_sync
|
|
114
125
|
}
|
115
126
|
end
|
116
127
|
|
128
|
+
def start_container(sync_name = nil)
|
129
|
+
init_sync_processes(sync_name)
|
130
|
+
@sync_processes.each { |sync_process|
|
131
|
+
sync_process.start_container
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
117
135
|
def run(sync_name = nil)
|
118
136
|
init_sync_processes(sync_name)
|
119
137
|
|
@@ -1,7 +1,6 @@
|
|
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'
|
5
4
|
require 'docker-sync/sync_strategy/unison'
|
6
5
|
# noinspection RubyResolve
|
7
6
|
require 'docker-sync/watch_strategy/fswatch'
|
@@ -36,15 +35,11 @@ module Docker_Sync
|
|
36
35
|
case @options['sync_strategy']
|
37
36
|
when 'rsync'
|
38
37
|
@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)
|
41
|
-
when 'unison'
|
42
|
-
@sync_strategy = Docker_Sync::SyncStrategy::Unison.new(@sync_name, @options)
|
43
38
|
else
|
44
|
-
@sync_strategy = Docker_Sync::SyncStrategy::
|
39
|
+
@sync_strategy = Docker_Sync::SyncStrategy::Unison.new(@sync_name, @options)
|
45
40
|
end
|
46
41
|
else
|
47
|
-
@sync_strategy = Docker_Sync::SyncStrategy::
|
42
|
+
@sync_strategy = Docker_Sync::SyncStrategy::Unison.new(@sync_name, @options)
|
48
43
|
end
|
49
44
|
end
|
50
45
|
|
@@ -55,16 +50,14 @@ module Docker_Sync
|
|
55
50
|
@watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
|
56
51
|
when 'disable','dummy'
|
57
52
|
@watch_strategy = Docker_Sync::WatchStrategy::Dummy.new(@sync_name, @options)
|
58
|
-
when 'unison'
|
59
|
-
@watch_strategy = Docker_Sync::WatchStrategy::Unison.new(@sync_name, @options)
|
60
53
|
else
|
61
|
-
@watch_strategy = Docker_Sync::WatchStrategy::
|
54
|
+
@watch_strategy = Docker_Sync::WatchStrategy::Unison.new(@sync_name, @options)
|
62
55
|
end
|
63
56
|
else
|
64
|
-
if @options['sync_strategy'] == '
|
65
|
-
@watch_strategy = Docker_Sync::WatchStrategy::Unison.new(@sync_name, @options)
|
66
|
-
else
|
57
|
+
if @options['sync_strategy'] == 'rsync'
|
67
58
|
@watch_strategy = Docker_Sync::WatchStrategy::Fswatch.new(@sync_name, @options)
|
59
|
+
else
|
60
|
+
@watch_strategy = Docker_Sync::WatchStrategy::Unison.new(@sync_name, @options)
|
68
61
|
end
|
69
62
|
end
|
70
63
|
end
|
@@ -93,6 +86,10 @@ module Docker_Sync
|
|
93
86
|
@sync_strategy.sync
|
94
87
|
end
|
95
88
|
|
89
|
+
def start_container
|
90
|
+
@sync_strategy.start_container
|
91
|
+
end
|
92
|
+
|
96
93
|
def watch
|
97
94
|
@watch_strategy.run
|
98
95
|
end
|
@@ -67,7 +67,7 @@ module Docker_Sync
|
|
67
67
|
# in the config - see start_container
|
68
68
|
#args.push("--usermap='*:#{@options['sync_user']}'") if @options.key?('sync_user')
|
69
69
|
#args.push("--groupmap='*:#{@options['sync_group']}'") if @options.key?('sync_group')
|
70
|
-
args.push("#{@options['src']}
|
70
|
+
args.push("'#{@options['src']}'")
|
71
71
|
args.push("rsync://#{@options['sync_host_ip']}:#{@options['sync_host_port']}/volume")
|
72
72
|
return args
|
73
73
|
end
|
@@ -96,7 +96,7 @@ module Docker_Sync
|
|
96
96
|
def expand_ignore_strings
|
97
97
|
expanded_ignore_strings = []
|
98
98
|
|
99
|
-
exclude_type = '
|
99
|
+
exclude_type = 'Name'
|
100
100
|
unless @options['sync_excludes_type'].nil?
|
101
101
|
exclude_type = @options['sync_excludes_type']
|
102
102
|
end
|
@@ -121,16 +121,25 @@ module Docker_Sync
|
|
121
121
|
args.push(@options['src'])
|
122
122
|
args.push('-auto')
|
123
123
|
args.push('-batch')
|
124
|
+
args.push(sync_prefer) if @options.key?('sync_prefer')
|
124
125
|
args.push(@options['sync_args']) if @options.key?('sync_args')
|
125
126
|
sync_host_port = get_host_port(get_container_name, UNISON_CONTAINER_PORT)
|
126
127
|
args.push("socket://#{@options['sync_host_ip']}:#{sync_host_port}")
|
127
128
|
|
128
129
|
if @options.key?('sync_group') || @options.key?('sync_groupid')
|
129
|
-
raise('Unison does not support
|
130
|
+
raise('Unison does not support sync_group, sync_groupid - please use rsync if you need that')
|
130
131
|
end
|
131
132
|
return args
|
132
133
|
end
|
133
134
|
|
135
|
+
def sync_prefer
|
136
|
+
case @options['sync_prefer']
|
137
|
+
when 'src' then "-prefer #{@options['src']}"
|
138
|
+
when 'dest' then "-prefer #{@options['dest']}"
|
139
|
+
else "-prefer #{@options['sync_prefer']}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
134
143
|
def start_container
|
135
144
|
say_status 'ok', 'Starting unison', :white
|
136
145
|
container_name = get_container_name
|
@@ -173,14 +182,25 @@ module Docker_Sync
|
|
173
182
|
say_status 'command', cmd, :white if @options['verbose']
|
174
183
|
`#{cmd}` || raise('Start failed')
|
175
184
|
say_status 'ok', "starting initial sync of #{container_name}", :white if @options['verbose']
|
176
|
-
#
|
177
|
-
|
185
|
+
# wait until container is started, then sync:
|
186
|
+
sync_host_port = get_host_port(get_container_name, UNISON_CONTAINER_PORT)
|
187
|
+
cmd = "unison -testserver #{@options['dest']} \"socket://#{@options['sync_host_ip']}:#{sync_host_port}\""
|
188
|
+
say_status 'command', cmd, :white if @options['verbose']
|
189
|
+
attempt = 0
|
190
|
+
loop do
|
191
|
+
stdout, stderr, exit_status = Open3.capture3(cmd)
|
192
|
+
break if exit_status == 0
|
193
|
+
attempt += 1
|
194
|
+
break if attempt > 5
|
195
|
+
sleep 1
|
196
|
+
end
|
178
197
|
sync
|
179
198
|
say_status 'success', 'Unison server started', :green
|
180
199
|
end
|
181
200
|
|
182
201
|
def get_host_port(container_name, container_port)
|
183
|
-
|
202
|
+
File.exist?('/usr/bin/sed') ? sed = '/usr/bin/sed' : sed = `which sed`.chomp # use macOS native sed in /usr/bin/sed first, fallback to sed in $PATH if it's not there
|
203
|
+
cmd = 'docker inspect --format=" {{ .NetworkSettings.Ports }} " ' + container_name + " | #{sed} " + '-E "s/.*map\[' + container_port + '[^ ]+ ([0-9]*)[^0-9].*/\1/"'
|
184
204
|
say_status 'command', cmd, :white if @options['verbose']
|
185
205
|
stdout, stderr, exit_status = Open3.capture3(cmd)
|
186
206
|
if not exit_status.success?
|
@@ -24,7 +24,6 @@ class UpdateChecker
|
|
24
24
|
# do not check the image if its the first run - since this it will be downloaded anyway
|
25
25
|
unless DockerSyncConfig::is_first_run
|
26
26
|
check_rsync_image
|
27
|
-
check_unison_onesided_image
|
28
27
|
check_unison_image
|
29
28
|
|
30
29
|
# stop if there was an update
|
@@ -73,18 +72,6 @@ class UpdateChecker
|
|
73
72
|
|
74
73
|
end
|
75
74
|
|
76
|
-
def check_unison_onesided_image
|
77
|
-
say_status 'ok','Checking if a newer unison:onesided image is available'
|
78
|
-
|
79
|
-
if system("docker pull eugenmayer/unison:onesided | grep 'Downloaded newer image for'")
|
80
|
-
say_status 'ok', 'Downloaded newer image for unison:onesided', :green
|
81
|
-
@newer_image_found = true
|
82
|
-
else
|
83
|
-
say_status 'ok', 'No newer image found - current image is up to date.'
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
75
|
def get_current_version
|
89
76
|
path = File.expand_path('../../../', __FILE__)
|
90
77
|
return File.read("#{path}/VERSION")
|
@@ -101,12 +88,13 @@ class UpdateChecker
|
|
101
88
|
# update the timestamp
|
102
89
|
now = DateTime.now
|
103
90
|
@config['update_last_check'] = now.iso8601(9)
|
91
|
+
|
104
92
|
DockerSyncConfig::global_config_save(@config)
|
105
93
|
|
106
94
|
check = docker_sync_update_check
|
107
95
|
if check.update_available
|
108
96
|
say_status 'warning',"There is an update (#{check.latest_version}) available (current version #{check.current_version}). Please update before you continue",:yellow
|
109
|
-
if yes?("Shall
|
97
|
+
if yes?("Shall I update docker-sync to #{check.latest_version} for you?")
|
110
98
|
system('gem update docker-sync')
|
111
99
|
say_status 'success','Successfully updated, please restart docker-sync and check the changelog at https://github.com/EugenMayer/docker-sync/wiki/5.-Changelog',:green
|
112
100
|
exit 0
|
@@ -23,7 +23,7 @@ class UpgradeChecker
|
|
23
23
|
def should_run
|
24
24
|
# get the update_status which is the version of the update hook which has been run already
|
25
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
|
26
|
+
if upgrade_status == '' || Gem::Version.new(upgrade_status) < Gem::Version.new(UpgradeChecker.get_current_version) # thats how we compare the version
|
27
27
|
return true
|
28
28
|
end
|
29
29
|
|
@@ -31,14 +31,14 @@ class UpgradeChecker
|
|
31
31
|
end
|
32
32
|
|
33
33
|
|
34
|
-
def get_current_version
|
34
|
+
def self.get_current_version
|
35
35
|
path = File.expand_path('../../../', __FILE__)
|
36
36
|
return File.read("#{path}/VERSION")
|
37
37
|
end
|
38
38
|
|
39
39
|
def docker_sync_update_check
|
40
40
|
gem_name = 'docker-sync'
|
41
|
-
current_version = get_current_version
|
41
|
+
current_version = UpgradeChecker.get_current_version
|
42
42
|
checker = GemUpdateChecker::Client.new(gem_name, current_version)
|
43
43
|
return checker
|
44
44
|
end
|
@@ -47,13 +47,21 @@ class UpgradeChecker
|
|
47
47
|
# this is the upgrade hook for the unison-unox introduction / rename of unison
|
48
48
|
if Gem::Version.new(last_upgraded_version) < Gem::Version.new('0.1.0')
|
49
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?')
|
50
|
+
unless Thor::Shell::Basic.new.yes?('Shall we continue? (y/N)')
|
51
51
|
exit 1
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
if Gem::Version.new(last_upgraded_version) < Gem::Version.new('0.2.0')
|
56
|
+
Thor::Shell::Basic.new.say_status 'warning', "A lot changed with 0.2.x! Unison is the default sync, unison-onesided has been REMOVED. If you have been using rsync, have been using unison excludes or you are not sure, please read the upgrade guide or your setup will go lala! : \n\n_Please_ read :): https://github.com/EugenMayer/docker-sync/wiki/1.2-Upgrade-Guide\n\n", :red
|
57
|
+
unless Thor::Shell::Basic.new.yes?('Shall we continue - DID you read it? (y/N)')
|
58
|
+
exit 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
55
63
|
# update the upgrade_status
|
56
|
-
@config['upgrade_status'] = "#{get_current_version}"
|
64
|
+
@config['upgrade_status'] = "#{UpgradeChecker.get_current_version}"
|
57
65
|
DockerSyncConfig::global_config_save(@config)
|
58
66
|
end
|
59
67
|
end
|
@@ -54,7 +54,7 @@ module Docker_Sync
|
|
54
54
|
args.push('-orIE')
|
55
55
|
args.push(@events_to_watch.map { |pattern| "--event #{pattern}" })
|
56
56
|
args.push(@options['watch_args']) if @options.key?('watch_args')
|
57
|
-
args.push(@options['src'])
|
57
|
+
args.push("'#{@options['src']}'")
|
58
58
|
sync_command = get_sync_cli_call
|
59
59
|
args.push(" | xargs -I -n1 #{sync_command} -n #{@sync_name} --config='#{@options['config_path']}'")
|
60
60
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'docker-sync/sync_manager'
|
2
|
+
require 'docker-sync/config'
|
3
|
+
require 'docker-sync/preconditions'
|
4
|
+
require 'docker-sync/update_check'
|
5
|
+
require 'docker-sync/upgrade_check'
|
6
|
+
require 'daemons'
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
class Daemon < Thor
|
10
|
+
class_option :dir, :aliases => '--dir', :default => './.docker-sync', :type => :string, :desc => 'Path to PID and OUTPUT file Directory'
|
11
|
+
class_option :logd, :aliases => '--logd', :default => true, :type => :boolean, :desc => 'To log OUPUT to file on Daemon or not'
|
12
|
+
class_option :app_name, :aliases => '--name', :default => 'daemon', :type => :string, :desc => 'App name used in PID and OUTPUT file name for Daemon'
|
13
|
+
class_option :logd, :aliases => '--logd', :default => true, :type => :boolean, :desc => 'To log OUPUT to file on Daemon or not'
|
14
|
+
class_option :config, :aliases => '-c',:default => nil, :type => :string, :desc => 'Path of the docker_sync config'
|
15
|
+
class_option :sync_name, :aliases => '-n',:type => :string, :desc => 'If given, only this sync configuration will be references/started/synced'
|
16
|
+
class_option :version, :aliases => '-v',:type => :boolean, :default => false, :desc => 'prints out the version of docker-sync and exits'
|
17
|
+
|
18
|
+
desc 'start', 'Start docker-sync daemon'
|
19
|
+
def start
|
20
|
+
opt = options.dup
|
21
|
+
opt.merge!(:daemon => true)
|
22
|
+
sync = Sync.new([], opt)
|
23
|
+
sync.start
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'stop', 'Stop docker-sync daemon'
|
27
|
+
def stop
|
28
|
+
opt = options.dup
|
29
|
+
opt.merge!(:daemon => true)
|
30
|
+
sync = Sync.new([], opt)
|
31
|
+
sync.stop
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'clean', 'Clean docker-sync daemon'
|
35
|
+
def clean
|
36
|
+
opt = options.dup
|
37
|
+
opt.merge!(:daemon => true)
|
38
|
+
sync = Sync.new([], opt)
|
39
|
+
sync.clean
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'log', 'Prints last 100 lines of daemon log. Only for use with docker-sync started in background.'
|
43
|
+
method_option :lines, :aliases => '--lines', :default => 100, :type => :numeric, :desc => 'Specify number of lines to tail'
|
44
|
+
method_option :follow, :aliases => '-f', :default => false, :type => :boolean, :desc => 'Specify if the logs should be streamed'
|
45
|
+
def log
|
46
|
+
opt = options.dup
|
47
|
+
sync = Sync.new([], opt)
|
48
|
+
sync.log
|
49
|
+
end
|
50
|
+
end
|
data/tasks/stack/stack.thor
CHANGED
@@ -8,10 +8,16 @@ require 'docker-sync/compose'
|
|
8
8
|
class Stack < Thor
|
9
9
|
class_option :config, :aliases => '-c', :default => nil, :type => :string, :desc => 'Path of the docker_sync config'
|
10
10
|
class_option :sync_name, :aliases => '-n', :type => :string, :desc => 'If given, only this sync configuration will be references/started/synced'
|
11
|
+
class_option :version, :aliases => '-v',:type => :boolean, :default => false, :desc => 'prints out the version of docker-sync and exits'
|
11
12
|
|
12
13
|
desc 'start', 'Start sync services, watcher and then your docker-compose defined stack'
|
13
14
|
|
14
15
|
def start
|
16
|
+
if options[:version]
|
17
|
+
puts UpgradeChecker.get_current_version
|
18
|
+
exit(0)
|
19
|
+
end
|
20
|
+
|
15
21
|
# do run update check in the start command only
|
16
22
|
updates = UpdateChecker.new
|
17
23
|
updates.run
|
@@ -37,6 +43,8 @@ class Stack < Thor
|
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
46
|
+
say_status 'note:', 'You can also run docker-sync in the background with docker-sync --daemon'
|
47
|
+
|
40
48
|
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
41
49
|
@sync_manager.run(options[:sync_name])
|
42
50
|
global_options = @sync_manager.global_options
|
@@ -63,6 +71,11 @@ class Stack < Thor
|
|
63
71
|
desc 'clean', 'compose down your app stack, stop and clean up all sync endpoints'
|
64
72
|
|
65
73
|
def clean
|
74
|
+
if options[:version]
|
75
|
+
puts UpgradeChecker.get_current_version
|
76
|
+
exit(0)
|
77
|
+
end
|
78
|
+
|
66
79
|
begin
|
67
80
|
Preconditions::check_all_preconditions
|
68
81
|
rescue Exception => e
|
data/tasks/sync/sync.thor
CHANGED
@@ -3,108 +3,128 @@ require 'docker-sync/config'
|
|
3
3
|
require 'docker-sync/preconditions'
|
4
4
|
require 'docker-sync/update_check'
|
5
5
|
require 'docker-sync/upgrade_check'
|
6
|
+
require 'daemons'
|
7
|
+
require 'fileutils'
|
6
8
|
|
7
9
|
class Sync < Thor
|
8
10
|
|
9
11
|
class_option :config, :aliases => '-c',:default => nil, :type => :string, :desc => 'Path of the docker_sync config'
|
10
12
|
class_option :sync_name, :aliases => '-n',:type => :string, :desc => 'If given, only this sync configuration will be references/started/synced'
|
13
|
+
class_option :version, :aliases => '-v',:type => :boolean, :default => false, :desc => 'prints out the version of docker-sync and exits'
|
11
14
|
|
12
15
|
desc 'start', 'Start all sync configurations in this project'
|
16
|
+
method_option :daemon, :aliases => '-d', :default => false, :type => :boolean, :desc => 'Run in the background'
|
17
|
+
method_option :app_name, :aliases => '--name', :default => 'daemon', :type => :string, :desc => 'App name used in PID and OUTPUT file name for Daemon'
|
18
|
+
method_option :dir, :aliases => '--dir', :default => './.docker-sync', :type => :string, :desc => 'Path to PID and OUTPUT file Directory'
|
19
|
+
method_option :logd, :aliases => '--logd', :default => true, :type => :boolean, :desc => 'To log OUPUT to file on Daemon or not'
|
13
20
|
def start
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
upgrades = UpgradeChecker.new
|
18
|
-
upgrades.run
|
19
|
-
begin
|
20
|
-
Preconditions::check_all_preconditions
|
21
|
-
rescue Exception => e
|
22
|
-
say_status 'error', e.message, :red
|
23
|
-
exit 1
|
21
|
+
if options[:version]
|
22
|
+
puts UpgradeChecker.get_current_version
|
23
|
+
exit(0)
|
24
24
|
end
|
25
|
+
# do run update check in the start command only
|
26
|
+
UpdateChecker.new().run
|
27
|
+
UpgradeChecker.new().run
|
28
|
+
|
29
|
+
config_path = config_preconditions # Preconditions and Define config_path from shared method
|
30
|
+
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
25
31
|
|
26
|
-
|
27
|
-
|
32
|
+
start_dir = Dir.pwd # Set start_dir variable to be equal to pre-daemonized folder, since daemonizing will change dir to '/'
|
33
|
+
if options['daemon']
|
34
|
+
daemonize
|
28
35
|
else
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
say_status 'note:', 'You can also run docker-sync in the background with --daemon', :white
|
37
|
+
end
|
38
|
+
|
39
|
+
Dir.chdir(start_dir) do # We want run these in pre-daemonized folder/directory since provided config_path might not be full_path
|
40
|
+
@sync_manager.run(options[:sync_name])
|
41
|
+
@sync_manager.join_threads
|
35
42
|
end
|
36
|
-
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
37
|
-
@sync_manager.run(options[:sync_name])
|
38
|
-
@sync_manager.join_threads
|
39
43
|
end
|
40
44
|
|
41
|
-
desc '
|
42
|
-
|
45
|
+
desc 'stop', 'Stop docker-sync daemon'
|
46
|
+
method_option :app_name, :aliases => '--name', :default => 'daemon', :type => :string, :desc => 'App name used in PID and OUTPUT file name for Daemon'
|
47
|
+
method_option :dir, :aliases => '--dir', :default => './.docker-sync', :type => :string, :desc => 'Path to PID and OUTPUT file Directory'
|
48
|
+
def stop
|
49
|
+
if options[:version]
|
50
|
+
puts UpgradeChecker.get_current_version
|
51
|
+
exit(0)
|
52
|
+
end
|
53
|
+
|
54
|
+
config_path = config_preconditions
|
55
|
+
sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
56
|
+
|
43
57
|
begin
|
44
|
-
|
45
|
-
|
46
|
-
say_status '
|
47
|
-
|
58
|
+
pid = File.read("#{options['dir']}/#{options['app_name']}.pid") # Read PID from PIDFILE created by Daemons
|
59
|
+
Process.kill(:INT, -(Process.getpgid(pid.to_i))) # Send INT signal to group PID, which means INT will be sent to all sub-processes and Threads spawned by docker-sync
|
60
|
+
say_status 'shutdown', 'Background dsync has been stopped'
|
61
|
+
rescue Errno::ESRCH, Errno::ENOENT => e
|
62
|
+
say_status 'error', e.message, :red # Rescue incase PIDFILE does not exist or there is no process with such PID
|
63
|
+
say_status(
|
64
|
+
'error', 'Check if your PIDFILE and process with such PID exists', :red
|
65
|
+
)
|
48
66
|
end
|
67
|
+
end
|
49
68
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
rescue Exception => e
|
56
|
-
say_status 'error', e.message, :red
|
57
|
-
return
|
58
|
-
end
|
69
|
+
desc 'sync', 'just sync - do not start a watcher though'
|
70
|
+
def sync
|
71
|
+
if options[:version]
|
72
|
+
puts UpgradeChecker.get_current_version
|
73
|
+
exit(0)
|
59
74
|
end
|
75
|
+
|
76
|
+
config_path = config_preconditions # Preconditions and Define config_path from shared method
|
77
|
+
|
60
78
|
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
61
79
|
@sync_manager.sync(options[:sync_name])
|
62
80
|
end
|
63
81
|
|
64
82
|
desc 'clean', 'Stop and clean up all sync endpoints'
|
65
83
|
def clean
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
say_status 'error', e.message, :red
|
70
|
-
exit 1
|
84
|
+
if options[:version]
|
85
|
+
puts UpgradeChecker.get_current_version
|
86
|
+
exit(0)
|
71
87
|
end
|
72
88
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
89
|
+
config_path = config_preconditions # Preconditions and Define config_path from shared method
|
90
|
+
|
91
|
+
# Look for any background syncs and stop them if we see them
|
92
|
+
dir = './.docker-sync'
|
93
|
+
files = Dir[File.join(dir, '*.pid')]
|
94
|
+
files.each do |pid_file|
|
95
|
+
pid = File.read(pid_file).to_i
|
96
|
+
Process.kill(:INT, -(Process.getpgid(pid)))
|
97
|
+
say_status 'shutdown', 'Background dsync has been stopped'
|
82
98
|
end
|
99
|
+
# Remove the .docker-sync directory
|
100
|
+
FileUtils.rm_r dir if File.directory?(dir)
|
101
|
+
|
83
102
|
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
84
103
|
@sync_manager.clean(options[:sync_name])
|
85
104
|
say_status 'success', 'Finished cleanup. Removed stopped, removed sync container and removed their volumes', :green
|
86
105
|
end
|
87
106
|
|
107
|
+
desc 'log', 'Prints last 100 lines of daemon log. Only for use with docker-sync started in background.'
|
108
|
+
method_option :lines, :aliases => '--lines', :default => 100, :type => :numeric, :desc => 'Specify number of lines to tail'
|
109
|
+
method_option :follow, :aliases => '-f', :default => false, :type => :boolean, :desc => 'Specify if the logs should be streamed'
|
110
|
+
def log
|
111
|
+
if options[:version]
|
112
|
+
puts UpgradeChecker.get_current_version
|
113
|
+
exit(0)
|
114
|
+
end
|
115
|
+
|
116
|
+
print_daemon_logs
|
117
|
+
end
|
118
|
+
|
88
119
|
desc 'list', 'List all sync-points of the project configuration path'
|
89
120
|
method_option :verbose, :default => false, :type => :boolean, :desc => 'Verbose output'
|
90
121
|
def list
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
say_status 'error', e.message, :red
|
95
|
-
exit 1
|
122
|
+
if options[:version]
|
123
|
+
puts UpgradeChecker.get_current_version
|
124
|
+
exit(0)
|
96
125
|
end
|
97
126
|
|
98
|
-
|
99
|
-
config_path = options[:config]
|
100
|
-
else
|
101
|
-
begin
|
102
|
-
config_path = DockerSyncConfig::project_config_path
|
103
|
-
rescue Exception => e
|
104
|
-
say_status 'error', e.message, :red
|
105
|
-
return
|
106
|
-
end
|
107
|
-
end
|
127
|
+
config_path = config_preconditions # Preconditions and Define config_path from shared method
|
108
128
|
|
109
129
|
say_status 'ok',"Found configuration at #{config_path}"
|
110
130
|
@sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
|
@@ -114,4 +134,71 @@ class Sync < Thor
|
|
114
134
|
print_table(config) if options['verbose']
|
115
135
|
end
|
116
136
|
end
|
137
|
+
|
138
|
+
no_tasks do
|
139
|
+
def config_preconditions # Moved shared preconditions block into separate method to have less/cleaner code
|
140
|
+
begin
|
141
|
+
Preconditions::check_all_preconditions
|
142
|
+
rescue Exception => e
|
143
|
+
say_status 'error', e.message, :red
|
144
|
+
exit 1
|
145
|
+
end
|
146
|
+
|
147
|
+
return options[:config] if options[:config]
|
148
|
+
|
149
|
+
begin
|
150
|
+
DockerSyncConfig::project_config_path
|
151
|
+
rescue Exception => e
|
152
|
+
say_status 'error', e.message, :red
|
153
|
+
exit 1
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def daemonize
|
158
|
+
# Create the directory for the logs/pid if it doesn't already exist:
|
159
|
+
FileUtils.mkpath(options['dir'])
|
160
|
+
|
161
|
+
# Check to see if we're already running:
|
162
|
+
if daemon_running?
|
163
|
+
say_status 'error', "docker-sync already started for this configuration", :red
|
164
|
+
exit 1
|
165
|
+
end
|
166
|
+
|
167
|
+
# If we're daemonizing, run a sync first to ensure the containers exist so that a docker-compose up won't fail:
|
168
|
+
@sync_manager.start_container(options[:sync_name])
|
169
|
+
# the existing strategies' start_container will also sync, but just in case a strategy doesn't, sync now:
|
170
|
+
@sync_manager.sync(options[:sync_name])
|
171
|
+
|
172
|
+
dopts = {
|
173
|
+
app_name: options['app_name'],
|
174
|
+
dir_mode: :normal,
|
175
|
+
dir: options['dir'],
|
176
|
+
log_output: options['logd'],
|
177
|
+
output_logfilename: "#{options['app_name']}.log"
|
178
|
+
} # List of options accepted by Daemonize, can be customized pretty nicely with provided CLI options
|
179
|
+
|
180
|
+
say_status 'success', 'Starting Docker-Sync in the background', :green
|
181
|
+
Daemons.daemonize(dopts)
|
182
|
+
end
|
183
|
+
|
184
|
+
def print_daemon_logs
|
185
|
+
unless daemon_running?
|
186
|
+
say_status 'error', "docker-sync is not running in daemon mode for this configuration", :red
|
187
|
+
exit 1
|
188
|
+
end
|
189
|
+
|
190
|
+
log_file = File.join(options['dir'], "#{options['app_name']}.log")
|
191
|
+
begin
|
192
|
+
system("tail #{options['follow'] ? '-f ' : ''}-n #{options['lines']} #{log_file}")
|
193
|
+
rescue Interrupt
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def daemon_running?
|
199
|
+
pid_file = Daemons::PidFile.find_files(options['dir'], options['app_name']).first || ''
|
200
|
+
|
201
|
+
File.file?(pid_file) && Daemons::Pid.running?(File.read(pid_file).to_i)
|
202
|
+
end
|
203
|
+
end
|
117
204
|
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.
|
4
|
+
version: 0.2.0.beta5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eugen Mayer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -84,33 +84,76 @@ dependencies:
|
|
84
84
|
- - '='
|
85
85
|
- !ruby/object:Gem::Version
|
86
86
|
version: 1.6.3
|
87
|
+
- !ruby/object:Gem::Dependency
|
88
|
+
name: dotenv
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - "~>"
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '2.1'
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.1.1
|
97
|
+
type: :runtime
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.1'
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 2.1.1
|
107
|
+
- !ruby/object:Gem::Dependency
|
108
|
+
name: daemons
|
109
|
+
requirement: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - "~>"
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '1.2'
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 1.2.3
|
117
|
+
type: :runtime
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '1.2'
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: 1.2.3
|
87
127
|
description: Sync your code live to docker-containers without losing any performance
|
88
128
|
on OSX
|
89
129
|
email: eugen.mayer@kontextwork.de
|
90
130
|
executables:
|
91
131
|
- docker-sync
|
92
132
|
- docker-sync-stack
|
133
|
+
- docker-sync-daemon
|
93
134
|
extensions: []
|
94
135
|
extra_rdoc_files: []
|
95
136
|
files:
|
96
137
|
- Thorfile
|
97
138
|
- VERSION
|
98
139
|
- bin/docker-sync
|
140
|
+
- bin/docker-sync-daemon
|
99
141
|
- bin/docker-sync-stack
|
100
142
|
- lib/docker-sync/compose.rb
|
101
143
|
- lib/docker-sync/config.rb
|
144
|
+
- lib/docker-sync/config_template.rb
|
102
145
|
- lib/docker-sync/execution.rb
|
103
146
|
- lib/docker-sync/preconditions.rb
|
104
147
|
- lib/docker-sync/sync_manager.rb
|
105
148
|
- lib/docker-sync/sync_process.rb
|
106
149
|
- lib/docker-sync/sync_strategy/rsync.rb
|
107
|
-
- lib/docker-sync/sync_strategy/unison-onesided.rb
|
108
150
|
- lib/docker-sync/sync_strategy/unison.rb
|
109
151
|
- lib/docker-sync/update_check.rb
|
110
152
|
- lib/docker-sync/upgrade_check.rb
|
111
153
|
- lib/docker-sync/watch_strategy/dummy.rb
|
112
154
|
- lib/docker-sync/watch_strategy/fswatch.rb
|
113
155
|
- lib/docker-sync/watch_strategy/unison.rb
|
156
|
+
- tasks/daemon/daemon.thor
|
114
157
|
- tasks/stack/stack.thor
|
115
158
|
- tasks/sync/sync.thor
|
116
159
|
homepage: https://github.com/EugenMayer/docker_sync
|
@@ -128,9 +171,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
171
|
version: '2.0'
|
129
172
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
173
|
requirements:
|
131
|
-
- - "
|
174
|
+
- - ">"
|
132
175
|
- !ruby/object:Gem::Version
|
133
|
-
version:
|
176
|
+
version: 1.3.1
|
134
177
|
requirements: []
|
135
178
|
rubyforge_project:
|
136
179
|
rubygems_version: 2.4.5.1
|
@@ -1,137 +0,0 @@
|
|
1
|
-
require 'thor/shell'
|
2
|
-
require 'docker-sync/preconditions'
|
3
|
-
require 'open3'
|
4
|
-
require 'terminal-notifier'
|
5
|
-
|
6
|
-
module Docker_Sync
|
7
|
-
module SyncStrategy
|
8
|
-
class Unison_Onesided
|
9
|
-
include Thor::Shell
|
10
|
-
|
11
|
-
@options
|
12
|
-
@sync_name
|
13
|
-
@watch_thread
|
14
|
-
UNISON_CONTAINER_PORT = '5000'
|
15
|
-
def initialize(sync_name, options)
|
16
|
-
@sync_name = sync_name
|
17
|
-
@options = options
|
18
|
-
# if a custom image is set, apply it
|
19
|
-
if @options.key?('image')
|
20
|
-
@docker_image = @options['image']
|
21
|
-
else
|
22
|
-
@docker_image = 'eugenmayer/unison:onesided'
|
23
|
-
end
|
24
|
-
begin
|
25
|
-
Preconditions::unison_available
|
26
|
-
rescue Exception => e
|
27
|
-
say_status 'error', "#{@sync_name} has been configured to sync with unison, but no unison available", :red
|
28
|
-
say_status 'error', e.message, :red
|
29
|
-
exit 1
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def run
|
34
|
-
start_container
|
35
|
-
sync
|
36
|
-
end
|
37
|
-
|
38
|
-
def sync
|
39
|
-
args = sync_options
|
40
|
-
cmd = 'unison ' + args.join(' ')
|
41
|
-
|
42
|
-
say_status 'command', cmd, :white if @options['verbose']
|
43
|
-
|
44
|
-
stdout, stderr, exit_status = Open3.capture3(cmd)
|
45
|
-
if not exit_status.success?
|
46
|
-
say_status 'error', "Error starting sync, exit code #{$?.exitstatus}", :red
|
47
|
-
say_status 'message', stderr
|
48
|
-
else
|
49
|
-
TerminalNotifier.notify(
|
50
|
-
"Synced #{@options['src']}", :title => @sync_name
|
51
|
-
) if @options['notify_terminal']
|
52
|
-
say_status 'ok', "Synced #{@options['src']}", :white
|
53
|
-
if @options['verbose']
|
54
|
-
say_status 'output', stdout
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def sync_options
|
60
|
-
args = []
|
61
|
-
|
62
|
-
unless @options['sync_excludes'].nil?
|
63
|
-
args = @options['sync_excludes'].map { |pattern| "-ignore='Path #{pattern}'" } + args
|
64
|
-
end
|
65
|
-
args.push(@options['src'])
|
66
|
-
args.push('-auto')
|
67
|
-
args.push('-batch')
|
68
|
-
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')
|
73
|
-
end
|
74
|
-
return args
|
75
|
-
end
|
76
|
-
|
77
|
-
def start_container
|
78
|
-
say_status 'ok', 'Starting unison', :white
|
79
|
-
container_name = get_container_name
|
80
|
-
volume_name = get_volume_name
|
81
|
-
running = `docker ps --filter 'status=running' --filter 'name=#{container_name}' --format "{{.Names}}" | grep '^#{container_name}$'`
|
82
|
-
if running == ''
|
83
|
-
say_status 'ok', "#{container_name} container not running", :white if @options['verbose']
|
84
|
-
exists = `docker ps --filter "status=exited" --filter "name=#{container_name}" --format "{{.Names}}" | grep '^#{container_name}$'`
|
85
|
-
if exists == ''
|
86
|
-
say_status 'ok', "creating #{container_name} container", :white if @options['verbose']
|
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}"
|
88
|
-
else
|
89
|
-
say_status 'ok', "starting #{container_name} container", :white if @options['verbose']
|
90
|
-
cmd = "docker start #{container_name}"
|
91
|
-
end
|
92
|
-
say_status 'command', cmd, :white if @options['verbose']
|
93
|
-
`#{cmd}` || raise('Start failed')
|
94
|
-
else
|
95
|
-
say_status 'ok', "#{container_name} container still running", :blue
|
96
|
-
end
|
97
|
-
say_status 'ok', "starting initial sync of #{container_name}", :white if @options['verbose']
|
98
|
-
# this sleep is needed since the container could be not started
|
99
|
-
sleep 1
|
100
|
-
sync
|
101
|
-
say_status 'success', 'Unison server started', :green
|
102
|
-
end
|
103
|
-
|
104
|
-
def get_container_name
|
105
|
-
return "#{@sync_name}"
|
106
|
-
end
|
107
|
-
|
108
|
-
def get_volume_name
|
109
|
-
return @sync_name
|
110
|
-
end
|
111
|
-
|
112
|
-
def stop_container
|
113
|
-
`docker stop #{get_container_name}`
|
114
|
-
end
|
115
|
-
|
116
|
-
def reset_container
|
117
|
-
stop_container
|
118
|
-
`docker rm #{get_container_name}`
|
119
|
-
`docker volume rm #{get_volume_name}`
|
120
|
-
end
|
121
|
-
|
122
|
-
def clean
|
123
|
-
reset_container
|
124
|
-
end
|
125
|
-
|
126
|
-
def stop
|
127
|
-
say_status 'ok', "Stopping sync container #{get_container_name}"
|
128
|
-
begin
|
129
|
-
stop_container
|
130
|
-
rescue Exception => e
|
131
|
-
say_status 'error', "Stopping failed of #{get_container_name}:", :red
|
132
|
-
puts e.message
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|