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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2c634ad99d5e88d69c686388e3346c96b3d2c30f
4
- data.tar.gz: d87d180c0ca6bbe42c7b29fd8d3d90ee203edfb0
3
+ metadata.gz: b1fcfca4a756b6eaaaa9a278bdea971d33e06117
4
+ data.tar.gz: aa304239c78e91cdabe5bbb59435d24bbcd9f485
5
5
  SHA512:
6
- metadata.gz: 0dbce940c38568427f7c2a845ed380caec3f41ccb6f5a3beb4d0ef16cf1913b5dc5ee4a2586785423529c242100710b09a4311d26dd436ed964e9820d86f85c9
7
- data.tar.gz: ffa6b76d564ee17c3fa1a34e56a37138de831996cab2da60765aeb635b62150accd41579377e5dbaf9789005f71845b8c0242c5034f93ba5acc27b01c2c41c2a
6
+ metadata.gz: 6d50f3fafeb4b8cbd59c76e5735391f5becf785dbbc09ab01d8cd596d1d14cc9b62f1ecaad22c5e60296ebc61417675b3220bdd7de6f84bad3c3b283866545c8
7
+ data.tar.gz: 90303f06c9d494dea39ad881ecfdf3880960c62d8b6868c1f3783941a6804f738de17ba91a82b63e6de02152cca579eeb287439d12b91ba31f63e8fd8278ae18
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.3
1
+ 0.3.0
@@ -0,0 +1,4 @@
1
+ require 'docker-sync/config/config_locator'
2
+ require 'docker-sync/config/global_config'
3
+ require 'docker-sync/config/project_config'
4
+ require 'docker-sync/preconditions/strategy'
@@ -0,0 +1,80 @@
1
+ require 'pathname'
2
+
3
+ module DockerSync
4
+ # helps us loading our config files, GlobalConfig and ProjectConfig
5
+ module ConfigLocator
6
+ ERROR_MISSING_PROJECT_CONFIG =
7
+ 'No docker-sync.yml configuration found in your path ( traversing up ) '\
8
+ 'Did you define it for your project?'.freeze
9
+
10
+ class << self
11
+ attr_accessor :global_config_path
12
+ # @return [String] The path to the global config location
13
+ def current_global_config_path
14
+ path = global_config_path
15
+ path = File.expand_path('~/.docker-sync-global.yml') if path.nil?
16
+ path
17
+ end
18
+
19
+ # @return [String] the path to the project configuration found
20
+ def lookup_project_config_path
21
+ files = project_config_find
22
+
23
+ raise ERROR_MISSING_PROJECT_CONFIG if files.empty?
24
+
25
+ files.pop
26
+ end
27
+
28
+ private
29
+
30
+
31
+ # this has been ruthlessly stolen from Thor/runner.rb - please do not hunt me for that :)
32
+ # returns a list of file paths matching the docker-sync.yml file. The return the first one we find while traversing
33
+ # the folder tree up
34
+ # @return [Array]
35
+ def project_config_find(skip_lookup = false)
36
+ # Finds docker-sync.yml by traversing from your current directory down to the root
37
+ # directory of your system. If at any time we find a docker-sync.yml file, we stop.
38
+ #
39
+ #
40
+ # ==== Example
41
+ #
42
+ # If we start at /Users/wycats/dev/thor ...
43
+ #
44
+ # 1. /Users/wycats/dev/thor
45
+ # 2. /Users/wycats/dev
46
+ # 3. /Users/wycats <-- we find a docker-sync.yml here, so we stop
47
+ #
48
+ # Suppose we start at c:\Documents and Settings\james\dev\docker-sync ...
49
+ #
50
+ # 1. c:\Documents and Settings\james\dev\docker-sync.yml
51
+ # 2. c:\Documents and Settings\james\dev
52
+ # 3. c:\Documents and Settings\james
53
+ # 4. c:\Documents and Settings
54
+ # 5. c:\ <-- no docker-sync.yml found!
55
+ #
56
+ docker_sync_files = []
57
+
58
+ unless skip_lookup
59
+ Pathname.pwd.ascend do |path|
60
+ docker_sync_files = globs_for_project_config(path).map { |g| Dir[g] }.flatten
61
+ break unless docker_sync_files.empty?
62
+ end
63
+ end
64
+
65
+ docker_sync_files
66
+ end
67
+
68
+ # Where to look for docker-sync.yml files.
69
+ #
70
+ def globs_for_project_config(path)
71
+ path = escape_globs(path)
72
+ ["#{path}/docker-sync.yml"]
73
+ end
74
+
75
+ def escape_globs(path)
76
+ path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&')
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,56 @@
1
+ require 'yaml'
2
+ require 'dotenv'
3
+
4
+ module DockerSync
5
+ module ConfigSerializer
6
+ class << self
7
+ # @param [String] config_path path to the yaml configuration to load
8
+ # @return [Object] returns a Yaml hashmap, expaneded by ENV vars
9
+ def default_deserializer_file(config_path)
10
+ config_string = File.read(config_path)
11
+ default_deserializer_string(config_string)
12
+ end
13
+
14
+ # @param [String] config_string the configuration string inf yaml format
15
+ # @return [Object] a yaml hashmap
16
+ def default_deserializer_string(config_string)
17
+ deserialize_config( expand_env_variables(config_string) )
18
+ end
19
+
20
+ private
21
+
22
+ # Replaces our tokens, in this case all ENV variables we defined. Find those in the string an replace
23
+ # them with then values from our ENV, including the dotenv file
24
+ # @param [String] config_string
25
+ # @return [String]
26
+ def expand_env_variables(config_string)
27
+ load_dotenv
28
+
29
+ env_hash = {}
30
+ ENV.each {|k,v| env_hash[k.to_sym] = v }
31
+ config_string.gsub!('${', '%{')
32
+ config_string % env_hash
33
+ end
34
+
35
+
36
+ # deserializes the configuration string, right now as a yaml formatted string
37
+ # @param [String] config_string
38
+ # @return [Object]
39
+ def deserialize_config(config_string)
40
+ # noinspection RubyResolve
41
+ YAML.load(config_string)
42
+ end
43
+
44
+ # Loads the dotenv file but also lets us overide the source not being .env but anything you put
45
+ # into the ENV variable DOCKER_SYNC_ENV_FILE
46
+ # @return [Object]
47
+ def load_dotenv
48
+ # TODO: ensure we do this once only
49
+ env_file = ENV.fetch('DOCKER_SYNC_ENV_FILE', '.env')
50
+
51
+ # noinspection RubyResolve
52
+ Dotenv.load(env_file)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,55 @@
1
+ require 'singleton'
2
+ require 'forwardable'
3
+
4
+ require 'docker-sync/config/config_locator'
5
+ require 'docker-sync/config/config_serializer'
6
+
7
+ module DockerSync
8
+ class GlobalConfig
9
+ extend Forwardable
10
+ include Singleton
11
+
12
+ # noinspection RubyStringKeysInHashInspection
13
+ DEFAULTS = {
14
+ 'update_check' => true,
15
+ 'update_last_check' => DateTime.new(2001, 1, 1).iso8601(9),
16
+ 'update_enforce' => true,
17
+ 'upgrade_status' => '',
18
+ }.freeze
19
+
20
+ attr_reader :config
21
+ private :config
22
+
23
+ def_delegators :@config, :[], :to_h
24
+
25
+ def self.load; instance end
26
+
27
+ def initialize
28
+ load_global_config
29
+ end
30
+
31
+ def load_global_config
32
+ @config_path = DockerSync::ConfigLocator.current_global_config_path
33
+ if File.exist?(@config_path)
34
+ @config = DockerSync::ConfigSerializer.default_deserializer_file(@config_path)
35
+ end
36
+
37
+ unless @config
38
+ @config = DEFAULTS.dup
39
+ @first_run = true
40
+ end
41
+ end
42
+
43
+ def first_run?
44
+ @first_run
45
+ end
46
+
47
+ # @param [Object] updates
48
+ # Updates and saves the configuration back to the file
49
+ def update!(updates)
50
+ @config.merge!(updates)
51
+
52
+ File.open(@config_path, 'w') {|f| f.write @config.to_yaml }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,123 @@
1
+ require 'singleton'
2
+ require 'docker-sync/config/config_locator'
3
+ require 'docker-sync/config/config_serializer'
4
+
5
+ module DockerSync
6
+ class ProjectConfig
7
+ extend Forwardable
8
+
9
+ REQUIRED_CONFIG_VERSION = '2'.freeze
10
+
11
+ ERROR_MISSING_CONFIG_VERSION =
12
+ "Your docker-sync.yml file does not include a version: \"#{REQUIRED_CONFIG_VERSION}\""\
13
+ '(Add this if you migrated from docker-sync 0.1.x)'.freeze
14
+
15
+ ERROR_MISMATCH_CONFIG_VERSION =
16
+ 'Your docker-sync.yml file does not match the required version '\
17
+ "(#{REQUIRED_CONFIG_VERSION}).".freeze
18
+
19
+ ERROR_MISSING_SYNCS = 'no syncs defined'.freeze
20
+
21
+ attr_reader :config, :config_path
22
+ private :config
23
+
24
+ def_delegators :@config, :[], :to_h
25
+
26
+ def initialize(config_path: nil, config_string: nil)
27
+ if config_string.nil?
28
+ config_path = DockerSync::ConfigLocator.lookup_project_config_path if config_path.nil?
29
+ load_project_config(config_path)
30
+ else
31
+ @config = DockerSync::ConfigSerializer.default_deserializer_string(config_string)
32
+ @config_path = nil
33
+ end
34
+
35
+ validate_config!
36
+ normalize_config!
37
+ end
38
+
39
+ def load_project_config(config_path = nil)
40
+ @config_path = config_path
41
+ return unless File.exist?(@config_path)
42
+ @config = DockerSync::ConfigSerializer.default_deserializer_file(@config_path)
43
+ end
44
+
45
+ def unison_required?
46
+ config['syncs'].any? { |name, sync_config|
47
+ sync_config['sync_strategy'] == 'unison' || sync_config['watch_strategy'] == 'unison'
48
+ }
49
+ end
50
+
51
+ def rsync_required?
52
+ config['syncs'].any? { |name, sync_config|
53
+ sync_config['sync_strategy'] == 'rsync'
54
+ }
55
+ end
56
+
57
+
58
+ private
59
+
60
+ def validate_config!
61
+ raise error_missing_given_config if config.nil?
62
+ raise ERROR_MISSING_CONFIG_VERSION unless config.key?('version')
63
+ raise ERROR_MISMATCH_CONFIG_VERSION unless config['version'].to_s == REQUIRED_CONFIG_VERSION
64
+ raise ERROR_MISSING_SYNCS unless config.key?('syncs')
65
+
66
+ validate_syncs_config!
67
+ end
68
+
69
+ def validate_syncs_config!
70
+ config['syncs'].each do |name, sync_config|
71
+ validate_sync_config(name, sync_config)
72
+ end
73
+ end
74
+
75
+ def validate_sync_config(name, sync_config)
76
+ config_mandatory = %w[src]
77
+ #TODO: Implement autodisovery for other strategies
78
+ config_mandatory.push('sync_host_port') if sync_config['sync_strategy'] == 'rsync'
79
+ config_mandatory.each do |key|
80
+ raise ("#{name} does not have #{key} configuration value set - this is mandatory") unless sync_config.key?(key)
81
+ end
82
+ end
83
+
84
+ def error_missing_given_config
85
+ "Config could not be loaded from #{config_path} - it does not exist"
86
+ end
87
+
88
+ def normalize_config!
89
+ config['syncs'].each do |name, sync_config|
90
+ config['syncs'][name] = normalize_sync_config(sync_config)
91
+ end
92
+ end
93
+
94
+ def normalize_sync_config(sync_config)
95
+ {
96
+ 'sync_strategy' => sync_strategy_for(sync_config),
97
+ 'watch_strategy' => watch_strategy_for(sync_config)
98
+ }.merge(sync_config)
99
+ end
100
+
101
+ def sync_strategy_for(sync_config)
102
+ case sync_config['sync_strategy']
103
+ when 'rsync' then 'rsync'
104
+ else 'unison'
105
+ end
106
+ end
107
+
108
+ def watch_strategy_for(sync_config)
109
+ if sync_config.key?('watch_strategy')
110
+ case sync_config['watch_strategy']
111
+ when 'fswatch' then 'fswatch'
112
+ when 'disable','dummy' then 'dummy'
113
+ else 'unison'
114
+ end
115
+ elsif sync_config['sync_strategy'] == 'rsync'
116
+ 'fswatch'
117
+ else
118
+ 'unison'
119
+ end
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,24 @@
1
+ module DockerSync
2
+ module Preconditions
3
+ class Linux
4
+ def check_all_preconditions(config)
5
+ end
6
+
7
+ def docker_available
8
+ end
9
+
10
+ def docker_running
11
+ end
12
+
13
+ def fswatch_available
14
+ end
15
+
16
+ def rsync_available
17
+ end
18
+
19
+ def unison_available
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,148 @@
1
+ require 'mkmf'
2
+ module DockerSync
3
+ module Preconditions
4
+ class Osx
5
+ def check_all_preconditions(config)
6
+ return unless should_run_precondition?
7
+
8
+ docker_available
9
+ docker_running
10
+
11
+ if config.unison_required?
12
+ unison_available
13
+ end
14
+
15
+ if config.rsync_required?
16
+ rsync_available
17
+ fswatch_available
18
+ end
19
+ end
20
+
21
+ def docker_available
22
+ if (find_executable0 'docker').nil?
23
+ raise('Could not find docker binary in path. Please install it, e.g. using "brew install docker" or install docker-for-mac')
24
+ end
25
+ end
26
+
27
+ def docker_running
28
+ `docker ps`
29
+ if $?.exitstatus > 0
30
+ raise('No docker daemon seems to be running. Did you start your docker-for-mac / docker-machine?')
31
+ end
32
+ end
33
+
34
+ def rsync_available
35
+ if should_run_precondition?
36
+ if (find_executable0 'rsync').nil?
37
+ raise('Could not find rsync binary in path. Please install it, e.g. using "brew install rsync"')
38
+ end
39
+ end
40
+ end
41
+
42
+ def unison_available
43
+ if should_run_precondition?
44
+ if (find_executable0 'unison').nil?
45
+ cmd1 = 'brew install unison"'
46
+
47
+ Thor::Shell::Basic.new.say_status 'warning', 'Could not find unison binary in $PATH. Trying to install now', :red
48
+ if Thor::Shell::Basic.new.yes?('I will install unison using brew for you? (y/N)')
49
+ system cmd1
50
+ else
51
+ raise('Please install it yourself using: brew install unison')
52
+ end
53
+ end
54
+
55
+ unox_available
56
+ end
57
+ end
58
+
59
+ def fswatch_available
60
+ if should_run_precondition?
61
+ if (find_executable0 'fswatch').nil?
62
+ cmd1 = 'brew install fswatch"'
63
+
64
+ Thor::Shell::Basic.new.say_status 'warning', 'No fswatch available. Install it by "brew install fswatch Trying to install now', :red
65
+ if Thor::Shell::Basic.new.yes?('I will install fswatch using brew for you? (y/N)')
66
+ system cmd1
67
+ else
68
+ raise('Please install it yourself using: brew install fswatch')
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ private
76
+
77
+ def should_run_precondition?(silent = false)
78
+ unless has_brew?
79
+ Thor::Shell::Basic.new.say_status 'inf', 'Not running any precondition checks since you have no brew and that is unsupported. Is all up to you know.', :white unless silent
80
+ return false
81
+ end
82
+ return true
83
+ end
84
+
85
+ def has_brew?
86
+ return find_executable0 'brew'
87
+ end
88
+
89
+
90
+ def unox_available
91
+ if should_run_precondition?
92
+ `brew list unox`
93
+ if $?.exitstatus > 0
94
+ unless (find_executable0 'unison-fsmonitor').nil?
95
+ # unox installed, but not using brew, we do not allow that anymore
96
+ Thor::Shell::Basic.new.say_status 'error', 'You install unison-fsmonitor (unox) not using brew. Please uninstall it and run docker-sync again, so we can install it for you', :red
97
+ exit 1
98
+ end
99
+ cmd1 = 'brew tap eugenmayer/dockersync && brew install eugenmayer/dockersync/unox'
100
+
101
+ Thor::Shell::Basic.new.say_status 'warning', 'Could not find unison-fsmonitor (unox) binary in $PATH. Trying to install now', :red
102
+ if Thor::Shell::Basic.new.yes?('I will install unox through brew for you? (y/N)')
103
+ system cmd1
104
+ else
105
+ raise('Please install it yourself using: brew tap eugenmayer/dockersync && brew install unox')
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ def install_pip(package, test = nil)
112
+ test ? `python -c 'import #{test}'` : `python -c 'import #{package}'`
113
+
114
+ unless $?.success?
115
+ Thor::Shell::Basic.new.say_status 'warning', "Could not find #{package}. Will try to install it using pip", :red
116
+ if find_executable0('python') == '/usr/bin/python'
117
+ Thor::Shell::Basic.new.say_status 'ok', 'You seem to use the system python, we will need sudo below'
118
+ sudo = true
119
+ cmd2 = "sudo easy_install pip && sudo pip install #{package}"
120
+ else
121
+ Thor::Shell::Basic.new.say_status 'ok', 'You seem to have a custom python, using non-sudo commands'
122
+ sudo = false
123
+ cmd2 = "easy_install pip && pip install #{package}"
124
+ end
125
+ if sudo
126
+ question = "I will ask you for you root password to install #{package} by running (This will ask for sudo, since we use the system python)"
127
+ else
128
+ question = "I will now install #{package} for you by running"
129
+ end
130
+
131
+ Thor::Shell::Basic.new.say_status 'info', "#{question}: `#{cmd2}\n\n"
132
+ if Thor::Shell::Basic.new.yes?('Shall I continue? (y/N)')
133
+ system cmd2
134
+ if $?.exitstatus > 0
135
+ raise("Failed to install #{package}, please file an issue with the output of the error")
136
+ end
137
+ test ? `python -c 'import #{test}'` : `python -c 'import #{package}'`
138
+ unless $?.success?
139
+ raise("Somehow I could not successfully install #{package} even though I tried. Please report this issue.")
140
+ end
141
+ else
142
+ raise("Please install #{package} manually, see https://github.com/EugenMayer/docker-sync/wiki/1.-Installation")
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end