Class: Vader::Sync

Inherits:
Object
  • Object
show all
Defined in:
lib/vader/sync.rb

Constant Summary collapse

B2D_BIN =
'/usr/local/bin/boot2docker'
B2D_ROOT =
ENV['VADER_B2D_ROOT'] || "/vader"
CONFIG_DIR =
File.expand_path(ENV['VADER_CONFIG_DIR'] || '~/.vader')
SYNC_LATENCY =
(ENV['VADER_SYNC_LATENCY'] || 0.1).to_f

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeSync

Returns a new instance of Sync.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/vader/sync.rb', line 14

def initialize
  glob = File.join(CONFIG_DIR, '*')

  # A hash of paths to watch and their boot2docker destinations
  # E.g.
  #  { '/Users/alice/Documents/my-project' => '/vader/my-project' }
  @watch_paths = Dir.glob(glob).inject({}) { |hash, config_path|
    b2d_path = File.join(B2D_ROOT, File.basename(config_path))

    begin
      watch_path = File.readlink(config_path)
      hash.merge(watch_path => b2d_path)
    rescue Errno::EINVAL
      hash
    end
  }

  @local_watch_paths = @watch_paths.keys

end

Instance Attribute Details

#local_watch_pathsObject (readonly)

Array of local paths



12
13
14
# File 'lib/vader/sync.rb', line 12

def local_watch_paths
  @local_watch_paths
end

#watch_pathsObject (readonly)

Hash of local paths and boot2docker destination



10
11
12
# File 'lib/vader/sync.rb', line 10

def watch_paths
  @watch_paths
end

Instance Method Details

#boot2docker_configObject



140
141
142
143
144
145
146
147
148
149
# File 'lib/vader/sync.rb', line 140

def boot2docker_config
  @boot2docker_config ||= `#{B2D_BIN} config`.split("\n").inject({}) {|hash, line|
    match = line.match(/(?<key>\S+)\s=\s"?(?<value>[^\s"]+)"?/)
    if match
      hash.merge(match[:key] => match[:value])
    else
      hash
    end
  }
end

#boot2docker_ssh_hostObject



163
164
165
# File 'lib/vader/sync.rb', line 163

def boot2docker_ssh_host
  "docker@localhost"
end

#boot2docker_ssh_optionsObject



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/vader/sync.rb', line 151

def boot2docker_ssh_options
  # From http://git.io/vJhs2
  [
    "-o", "IdentitiesOnly=yes",
    "-o", "StrictHostKeyChecking=no",
    "-o", "UserKnownHostsFile=/dev/null",
    "-o", "LogLevel=quiet",
    "-p", boot2docker_config['SSHPort'],
    "-i", boot2docker_config['SSHKey']
  ].join(' ')
end

#handle_fs_event(full_paths) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/vader/sync.rb', line 81

def handle_fs_event(full_paths)
  puts "handling event for #{full_paths.inspect}" if verbose?

  # full_paths is potentially large, so try to iterated efficiently
  matched_paths = []
  full_paths.each do |full_path|
    # Skip if we've already mathed this path
    next if matched_paths.any? {|p| full_path.start_with?(p) }

    matched_path = local_watch_paths.detect{|p| full_path.start_with?(p) }
    matched_paths << matched_path  if matched_path

    if full_path.start_with? CONFIG_DIR
      puts "Vader config change detected, restarting."
      exit 0
    end
  end

  matched_paths.each {|path| sync(path) }
end

#runObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/vader/sync.rb', line 35

def run
  # Make sure boot2docker is running
  waiting = false
  while `#{B2D_BIN} status`.chomp != 'running'
    puts "Waiting until boot2docker is running" unless waiting
    waiting = true
    sleep 2
  end


  # Initialize boot2docker for vader
  cmds = [
    'tce-load -wi rsync', # Install rsync
    # Make vader destination and symlink to /vader
    'sudo mkdir -p /mnt/sda1/vader',
    'sudo ln -sf /mnt/sda1/vader /',
    'sudo chown docker:staff /vader'
  ]

  safe_paths = @watch_paths.values.map{|path| Shellwords.shellescape(path)}.join(' ')
  cmds << "mkdir -p #{safe_paths}" unless safe_paths.empty?
  cmds = cmds.join(' && ')

  puts "Running #{cmds}" if verbose?
  `ssh #{boot2docker_ssh_options} #{boot2docker_ssh_host} "#{cmds}"`

  unless $?.success?
    puts "Error initalizing boot2docker for vader"
    exit 1
  end

  # Do initial sync of each local watch path
  local_watch_paths.each do |path|
    puts "Doing initial sync of #{path}"
    sync(path)
  end

  # Watch for fs changes
  puts "Watching #{local_watch_paths.inspect} + #{CONFIG_DIR}" if verbose?
  fsevent = FSEvent.new
  fsevent.watch local_watch_paths + [CONFIG_DIR], latency: SYNC_LATENCY do |paths|
    handle_fs_event(paths)
  end
  fsevent.run
end

#sync(path) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/vader/sync.rb', line 102

def sync(path)
  puts "Syncing #{path}"
  exclude_path = File.join(path, '.rsync-excludes')

  rsync_options = [
    '-av',
    '--delete',
    "--rsh=ssh #{boot2docker_ssh_options}"
  ]

  if File.exists?(exclude_path)
    rsync_options << "--exclude-from=#{exclude_path}"
  end

  rsync_options += [
    "#{path}/",
    "#{boot2docker_ssh_host}:#{watch_paths[path]}"
  ]

  puts rsync_options.inspect if verbose?
  options = verbose? ? {} : {out: '/dev/null'}

  start = Time.now
  pid = spawn("rsync", *rsync_options, options)

  Process.wait(pid)
  puts "Finished in #{Time.now - start} seconds" if verbose?

  unless $?.success?
    puts "Error syncing #{path}, restarting."
    exit 1
  end
end

#verbose?Boolean

Returns:

  • (Boolean)


136
137
138
# File 'lib/vader/sync.rb', line 136

def verbose?
  ENV['VADER_VERBOSE']
end