docker-sync 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,12 @@
1
+ require 'docker-sync'
1
2
  require 'docker-sync/sync_manager'
2
- require 'docker-sync/config'
3
- require 'docker-sync/preconditions'
3
+ require 'docker-sync/preconditions/strategy'
4
4
  require 'docker-sync/update_check'
5
5
  require 'docker-sync/upgrade_check'
6
6
  require 'daemons'
7
7
  require 'fileutils'
8
+ require 'docker-sync/config/project_config'
9
+ require 'timeout'
8
10
 
9
11
  class Sync < Thor
10
12
 
@@ -27,11 +29,11 @@ class Sync < Thor
27
29
  def start
28
30
  print_version if options[:version]
29
31
  # do run update check in the start command only
30
- UpdateChecker.new().run
31
- UpgradeChecker.new().run
32
+ UpdateChecker.new.run
33
+ UpgradeChecker.new.run
32
34
 
33
- config_path = config_preconditions # Preconditions and Define config_path from shared method
34
- @sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
35
+ config = config_preconditions
36
+ @sync_manager = Docker_sync::SyncManager.new(config: config)
35
37
 
36
38
  start_dir = Dir.pwd # Set start_dir variable to be equal to pre-daemonized folder, since daemonizing will change dir to '/'
37
39
  if options['daemon']
@@ -52,18 +54,17 @@ class Sync < Thor
52
54
  def stop
53
55
  print_version if options[:version]
54
56
 
55
- config_path = config_preconditions
56
- sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
57
+ config = config_preconditions
58
+ sync_manager = Docker_sync::SyncManager.new(config: config)
57
59
 
58
60
  begin
59
61
  pid = File.read("#{options['dir']}/#{options['app_name']}.pid") # Read PID from PIDFILE created by Daemons
60
62
  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
61
- say_status 'shutdown', 'Background dsync has been stopped'
63
+ wait_for_process_termination(pid.to_i)
62
64
  rescue Errno::ESRCH, Errno::ENOENT => e
63
65
  say_status 'error', e.message, :red # Rescue incase PIDFILE does not exist or there is no process with such PID
64
- say_status(
65
- 'error', 'Check if your PIDFILE and process with such PID exists', :red
66
- )
66
+ say_status 'error', 'Check if your PIDFILE and process with such PID exists', :red
67
+ exit(69) # EX_UNAVAILABLE (see `man sysexits` or `/usr/include/sysexits.h`)
67
68
  end
68
69
  end
69
70
 
@@ -71,9 +72,9 @@ class Sync < Thor
71
72
  def sync
72
73
  print_version if options[:version]
73
74
 
74
- config_path = config_preconditions # Preconditions and Define config_path from shared method
75
+ config = config_preconditions
75
76
 
76
- @sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
77
+ @sync_manager = Docker_sync::SyncManager.new(config: config)
77
78
  @sync_manager.sync(options[:sync_name])
78
79
  end
79
80
 
@@ -81,7 +82,7 @@ class Sync < Thor
81
82
  def clean
82
83
  print_version if options[:version]
83
84
 
84
- config_path = config_preconditions # Preconditions and Define config_path from shared method
85
+ config = config_preconditions
85
86
 
86
87
  # Look for any background syncs and stop them if we see them
87
88
  dir = './.docker-sync'
@@ -94,7 +95,7 @@ class Sync < Thor
94
95
  # Remove the .docker-sync directory
95
96
  FileUtils.rm_r dir if File.directory?(dir)
96
97
 
97
- @sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
98
+ @sync_manager = Docker_sync::SyncManager.new(config: config)
98
99
  @sync_manager.clean(options[:sync_name])
99
100
  say_status 'success', 'Finished cleanup. Removed stopped, removed sync container and removed their volumes', :green
100
101
  end
@@ -116,10 +117,10 @@ class Sync < Thor
116
117
  def list
117
118
  print_version if options[:version]
118
119
 
119
- config_path = config_preconditions # Preconditions and Define config_path from shared method
120
+ config = config_preconditions
120
121
 
121
122
  say_status 'ok',"Found configuration at #{config_path}"
122
- @sync_manager = Docker_sync::SyncManager.new(:config_path => config_path)
123
+ @sync_manager = Docker_sync::SyncManager.new(config: config)
123
124
  @sync_manager.get_sync_points.each do |name, config|
124
125
  say_status name, "On address #{config['sync_host_ip']}:#{config['sync_host_port']}",:white unless options['verbose']
125
126
  puts "\n---------------[#{name}] #{config['sync_host_ip']}:#{config['sync_host_port']} ---------------\n" if options['verbose']
@@ -130,16 +131,9 @@ class Sync < Thor
130
131
  no_tasks do
131
132
  def config_preconditions # Moved shared preconditions block into separate method to have less/cleaner code
132
133
  begin
133
- Preconditions::check_all_preconditions
134
- rescue Exception => e
135
- say_status 'error', e.message, :red
136
- exit 1
137
- end
138
-
139
- return options[:config] if options[:config]
140
-
141
- begin
142
- DockerSyncConfig::project_config_path
134
+ DockerSync::ProjectConfig.new(config_path: options[:config]).tap do |config|
135
+ DockerSync::Preconditions::Strategy.instance.check_all_preconditions(config)
136
+ end
143
137
  rescue Exception => e
144
138
  say_status 'error', e.message, :red
145
139
  exit 1
@@ -191,5 +185,29 @@ class Sync < Thor
191
185
  pid_file = Daemons::PidFile.find_files(options['dir'], options['app_name']).first || ''
192
186
  File.file?(pid_file) && Daemons::Pid.running?(File.read(pid_file).to_i)
193
187
  end
188
+
189
+ def wait_for_process_termination(pid)
190
+ print 'Waiting for background docker-sync to terminate'
191
+ Timeout::timeout(30) do
192
+ loop do
193
+ if process_dead?(pid)
194
+ puts
195
+ say_status 'shutdown', 'Background docker-sync has been stopped'
196
+ return
197
+ else
198
+ sleep 1
199
+ print '.'
200
+ end
201
+ end
202
+ end
203
+ rescue Timeout::Error
204
+ puts
205
+ say_status 'error', 'Background docker-sync daemon failed to stop within 30 seconds', :red
206
+ exit 70 # EX_SOFTWARE (according to `man sysexits`)
207
+ end
208
+
209
+ def process_dead?(pid)
210
+ !system("ps -p #{pid} > /dev/null")
211
+ end
194
212
  end
195
213
  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.2.3
4
+ version: 0.3.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: 2017-03-27 00:00:00.000000000 Z
11
+ date: 2017-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -124,6 +124,20 @@ dependencies:
124
124
  - - ">="
125
125
  - !ruby/object:Gem::Version
126
126
  version: 1.2.3
127
+ - !ruby/object:Gem::Dependency
128
+ name: os
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
127
141
  description: Sync your code live to docker-containers without losing any performance
128
142
  on OSX
129
143
  email: eugen.mayer@kontextwork.de
@@ -139,11 +153,16 @@ files:
139
153
  - bin/docker-sync
140
154
  - bin/docker-sync-daemon
141
155
  - bin/docker-sync-stack
156
+ - lib/docker-sync.rb
142
157
  - lib/docker-sync/compose.rb
143
- - lib/docker-sync/config.rb
144
- - lib/docker-sync/config_template.rb
158
+ - lib/docker-sync/config/config_locator.rb
159
+ - lib/docker-sync/config/config_serializer.rb
160
+ - lib/docker-sync/config/global_config.rb
161
+ - lib/docker-sync/config/project_config.rb
145
162
  - lib/docker-sync/execution.rb
146
- - lib/docker-sync/preconditions.rb
163
+ - lib/docker-sync/preconditions/preconditions_linux.rb
164
+ - lib/docker-sync/preconditions/preconditions_osx.rb
165
+ - lib/docker-sync/preconditions/strategy.rb
147
166
  - lib/docker-sync/sync_manager.rb
148
167
  - lib/docker-sync/sync_process.rb
149
168
  - lib/docker-sync/sync_strategy/rsync.rb
@@ -1,120 +0,0 @@
1
- require 'pp'
2
- require 'pathname'
3
- require 'yaml'
4
- require 'dotenv'
5
- require 'docker-sync/config_template'
6
-
7
- # this has basically completely reused from Thor::runner.rb - thank you!
8
- module DockerSyncConfig
9
-
10
- def initialize(options)
11
- load_dotenv
12
- end
13
-
14
- def self.load_dotenv
15
- env_file = ENV.fetch('DOCKER_SYNC_ENV_FILE', '.env')
16
- Dotenv.load(env_file)
17
- end
18
-
19
- def self.global_config_location
20
- return File.expand_path('~/.docker-sync-global.yml')
21
- end
22
-
23
- def self.is_first_run
24
- global_config_path = global_config_location
25
- return !File.exist?(global_config_path)
26
- end
27
-
28
- def self.global_config
29
- global_config_path = global_config_location
30
- date = DateTime.new(2001, 1, 1) #paste
31
- # noinspection RubyStringKeysInHashInspection
32
- defaults = {'update_check'=>true, 'update_last_check' => date.iso8601(9), 'update_enforce' => true}
33
- if File.exist?(global_config_path)
34
- config = ConfigTemplate::interpolate_config_file(global_config_path)
35
- config = defaults.merge(config)
36
- return config
37
- else
38
- return defaults
39
- end
40
- end
41
-
42
- def self.global_config_save(config)
43
- global_config_path = global_config_location
44
- File.open(global_config_path, 'w') {|f| f.write config.to_yaml }
45
- end
46
-
47
-
48
- def self.project_required_config_version
49
- return '2'
50
- end
51
-
52
- def self.project_ensure_configuration_version_compatibility(config)
53
- return false unless config.key?('version')
54
- return false if config['version'].to_s != project_required_config_version.to_s
55
- return true
56
- end
57
-
58
- def self.project_config_path
59
- files = project_config_find
60
- if files.length > 0
61
- path = files.pop
62
- else
63
- raise('No docker-sync.yml configuration found in your path ( traversing up ) Did you define it for your project?')
64
- end
65
-
66
- begin
67
- config = YAML.load_file(path)
68
- raise "Version of docker-sync.yml does not match the reqiured one" unless project_ensure_configuration_version_compatibility(config)
69
- rescue Exception => e
70
- 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)"
71
- end
72
- return path
73
- end
74
-
75
- # this has been ruthlessly stolen from Thor/runner.rb - please do not hunt me for that :)
76
- def self.project_config_find(skip_lookup = false)
77
- # Finds docker-sync.yml by traversing from your current directory down to the root
78
- # directory of your system. If at any time we find a docker-sync.yml file, we stop.
79
- #
80
- #
81
- # ==== Example
82
- #
83
- # If we start at /Users/wycats/dev/thor ...
84
- #
85
- # 1. /Users/wycats/dev/thor
86
- # 2. /Users/wycats/dev
87
- # 3. /Users/wycats <-- we find a docker-sync.yml here, so we stop
88
- #
89
- # Suppose we start at c:\Documents and Settings\james\dev\docker-sync ...
90
- #
91
- # 1. c:\Documents and Settings\james\dev\docker-sync.yml
92
- # 2. c:\Documents and Settings\james\dev
93
- # 3. c:\Documents and Settings\james
94
- # 4. c:\Documents and Settings
95
- # 5. c:\ <-- no docker-sync.yml found!
96
- #
97
- docker_sync_files = []
98
-
99
- unless skip_lookup
100
- Pathname.pwd.ascend do |path|
101
- docker_sync_files = globs_for_project_config(path).map { |g| Dir[g] }.flatten
102
- break unless docker_sync_files.empty?
103
- end
104
- end
105
-
106
- files = []
107
- files += docker_sync_files
108
- end
109
-
110
- # Where to look for docker-sync.yml files.
111
- #
112
- def self.globs_for_project_config(path)
113
- path = escape_globs(path)
114
- ["#{path}/docker-sync.yml"]
115
- end
116
-
117
- def self.escape_globs(path)
118
- path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&')
119
- end
120
- end
@@ -1,17 +0,0 @@
1
- require "yaml"
2
- require 'dotenv'
3
-
4
- module ConfigTemplate
5
- def self.interpolate_config_string(config_string)
6
- env_hash = {}
7
- ENV.each {|k,v| env_hash[k.to_sym] = v }
8
- config_string.gsub!('${', '%{')
9
- config_string = config_string % env_hash
10
- return YAML.load(config_string)
11
- end
12
- def self.interpolate_config_file(config_path)
13
- # assuming the checks that file exist have already been performed
14
- config_string = File.read(config_path)
15
- self.interpolate_config_string(config_string)
16
- end
17
- end
@@ -1,100 +0,0 @@
1
- require 'mkmf'
2
-
3
- module Preconditions
4
- def self.check_all_preconditions
5
- docker_available
6
- docker_running
7
- unison_available
8
- unox_available
9
- macfsevents_available
10
- end
11
-
12
- def self.docker_available
13
- if (find_executable0 'docker').nil?
14
- raise('Could not find docker binary in path. Please install it, e.g. using "brew install docker" or install docker-for-mac')
15
- end
16
- end
17
-
18
- def self.docker_running
19
- `docker ps`
20
- if $?.exitstatus > 0
21
- raise('No docker daemon seems to be running. Did you start your docker-for-mac / docker-machine?')
22
- end
23
- end
24
-
25
- def self.fswatch_available
26
- if (find_executable0 'fswatch').nil?
27
- raise('No fswatch available. Install it by "brew install fswatch"')
28
- end
29
- end
30
-
31
- def self.docker_sync_available
32
- if (find_executable0 'docker-sync').nil?
33
- raise('No docker-sync available. Install it by "gem install docker-sync"')
34
- end
35
- end
36
-
37
- def self.rsync_available
38
- if (find_executable0 'rsync').nil?
39
- raise('Could not find rsync binary in path. Please install it, e.g. using "brew install rsync"')
40
- end
41
- end
42
-
43
- def self.unison_available
44
- if (find_executable0 'unison').nil?
45
- raise('Could not find unison binary in path. Please install it, e.g. using "brew install unison"')
46
- end
47
- end
48
-
49
- def self.unox_available
50
- if (find_executable0 'unison-fsmonitor').nil?
51
- cmd1 = 'curl "https://raw.githubusercontent.com/hnsl/unox/master/unox.py" -o "/usr/local/bin/unison-fsmonitor" \
52
- && chmod +x /usr/local/bin/unison-fsmonitor'
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)')
56
- system cmd1
57
- else
58
- raise("Please install it, see https://github.com/hnsl/unox, or simply run :\n #{cmd1}")
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
- if find_executable0('python') == '/usr/bin/python'
69
- Thor::Shell::Basic.new.say_status 'ok','You seem to use the system python, we will need sudo below'
70
- sudo = true
71
- cmd2 = 'sudo easy_install pip && sudo pip install macfsevents'
72
- else
73
- Thor::Shell::Basic.new.say_status 'ok','You seem to have a custom python, using non-sudo commands'
74
- sudo = false
75
- cmd2 = 'easy_install pip && pip install macfsevents'
76
- end
77
- if sudo
78
- 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)'
79
- else
80
- question = 'I will now install macfsevents for you by running'
81
- end
82
-
83
- Thor::Shell::Basic.new.say_status 'info', "#{question}: `#{cmd2}\n\n"
84
- if Thor::Shell::Basic.new.yes?('Shall i continue? (y/N)')
85
- system cmd2
86
- if $?.exitstatus > 0
87
- raise('Failed to install macfsevents, please file an issue with the output of the error')
88
- end
89
- `python -c 'import fsevents'`
90
- unless $?.success?
91
- raise('Somehow could not successfully install macfsevents even though i treed. Please report this issue')
92
- end
93
- else
94
- raise('Please install macfsevents manually, see https://github.com/EugenMayer/docker-sync/wiki/1.-Installation')
95
- end
96
- end
97
-
98
-
99
- end
100
- end