vagrant-dnsdock-hostupdater 0.0.23 → 0.0.24

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: 024acc29cae06921872cb4dcd8ffae03c9d3651e
4
- data.tar.gz: 9e7f158d7030a282d9daab8a51358bc0120f07f5
3
+ metadata.gz: a0dced9da0ff736e5641dd7d6e5e0154f9db87a4
4
+ data.tar.gz: ab09fc9827e6618275ca8eb3d86fc8d0f6c0afa6
5
5
  SHA512:
6
- metadata.gz: cf9a3f80dc458ef8323318bf49bc01eac8d4a0dd314f086c73afc2ad04fa5f1d01ef44bfcb72ed0e0c38d0a2b7aff86044423d104520f0254abcbcde0614c49b
7
- data.tar.gz: 2f222d9b676aefc501715f5b8afcf2a027d7f5fc76c984d6bd04b9a99887e758465b55796cedc382cba27a3f4bde1a44409bb0cc69f0017e798d69d229060f57
6
+ metadata.gz: 8c9a89a41498583338779437ed08ca5b08a3fc313b20ae7f2f75194b8fdbd4a6b4c030de210b76eac59c93c1829b49f2b1dc2872fb31669ef46737bead77e954
7
+ data.tar.gz: b8fac1c93d16df2f9f056ae1c990fb364156e339599fc7c00b49fc3e45269fe62f6246cba578af698cdfd29e9d120dc343bcf209307fca7d476ebbd9f6e2b407
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- vagrant-dnsdock-hostupdater (0.0.23)
4
+ vagrant-dnsdock-hostupdater (0.0.24)
5
5
  docker-api (~> 1.33)
6
6
  linebreak (~> 2.1.0)
7
7
  log4r (~> 1.1.10)
data/lib/client.rb ADDED
@@ -0,0 +1,4 @@
1
+ require_relative 'host-manager'
2
+
3
+ HostManager::Client
4
+ .create('192.168.200.1', 2991)
@@ -0,0 +1,51 @@
1
+ require_relative 'host-manager'
2
+ require 'yaml'
3
+ require 'pp'
4
+
5
+ module HostManager
6
+ class DockerEvent
7
+
8
+ def self.update(ip, port, host_suffix)
9
+ client = HostManager::Client.create(ip, port)
10
+ client.host_suffix host_suffix
11
+ client.sync_hosts Docker::Container.all({:all => true})
12
+ client.close
13
+ end
14
+
15
+ def self.run(ip, port, host_suffix)
16
+ # sync existing host entries on start (normally this will remove old left-over entries)
17
+ self.update(ip, port, host_suffix)
18
+
19
+ sleep(2)
20
+ loop {
21
+ run_once(ip, port, host_suffix)
22
+ }
23
+ end
24
+
25
+ private
26
+ def self.run_once(ip, port, host_suffix)
27
+ begin
28
+ Docker::Event.stream { |event|
29
+ HostManager.log.info 'Connected to docker socket. Listening for events.'
30
+ # respond to all container events
31
+
32
+ begin
33
+ if event.type == 'container' && %w(start die).include?(event.action)
34
+ self.update(ip, port, host_suffix)
35
+ end
36
+
37
+ rescue => e
38
+ puts e.inspect
39
+ end
40
+
41
+ break
42
+ }
43
+
44
+ rescue Docker::Error::TimeoutError
45
+ HostManager.log.info 'Docker socket connection timed out whilst listening for events. Reconnecting...'
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ HostManager::DockerEvent.run('192.168.200.1', 2991, '.local.signal.sh')
@@ -0,0 +1,298 @@
1
+ require 'socket' # Get sockets from stdlib
2
+ require 'json'
3
+ require_relative 'hosts/hosts'
4
+ require 'docker'
5
+ require 'logger'
6
+ require 'log4r'
7
+
8
+ module OS
9
+ def OS.windows?
10
+ (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
11
+ end
12
+
13
+ def OS.mac?
14
+ (/darwin/ =~ RUBY_PLATFORM) != nil
15
+ end
16
+
17
+ def OS.unix?
18
+ !OS.windows?
19
+ end
20
+
21
+ def OS.linux?
22
+ OS.unix? and not OS.mac?
23
+ end
24
+ end
25
+
26
+ module HostManager
27
+
28
+ def self.log
29
+ unless @log
30
+ @log = Log4r::Logger.new('main')
31
+ @log.outputters << Log4r::Outputter.stdout
32
+ @log.outputters << Log4r::FileOutputter.new('logmain', :filename => 'host-manager.log')
33
+ end
34
+
35
+ @log
36
+ end
37
+
38
+ def self.comment_prefix
39
+ 'Managed by Vagrant plugin - do not remove.'
40
+ end
41
+
42
+ def self.host_file_path
43
+ OS.windows? ? `echo %SystemRoot%\\System32\\drivers\\etc\\hosts`.chomp : '/etc/hosts'
44
+ end
45
+
46
+ class Base
47
+
48
+ def log
49
+ HostManager.log
50
+ end
51
+
52
+ def host_name(container_name)
53
+ if container_name.include?('.')
54
+ container_name
55
+ else
56
+ sanitize_host_value(container_name) + host_suffix
57
+ end
58
+ end
59
+
60
+ def compose_host_names(container)
61
+ names = []
62
+ labels = container.info['Config']['Labels']
63
+
64
+ name = labels['com.docker.compose.project']
65
+ service = labels['com.docker.compose.service']
66
+
67
+
68
+ if name != nil && service != nil
69
+ service = sanitize_host_value(service)
70
+ name = sanitize_host_value(name)
71
+
72
+ container_num = labels['com.docker.compose.container-number']
73
+ if container_num == '1'
74
+ names.push("#{service}.#{name}#{host_suffix}")
75
+ else
76
+ names.push("#{service}-#{container_num}.#{name}#{host_suffix}")
77
+ end
78
+ else
79
+ names.push(host_name(container.info['Name']))
80
+ end
81
+
82
+ names
83
+ end
84
+
85
+ def host_suffix(suffix = nil)
86
+ if suffix != nil
87
+ @domain_suffix = suffix
88
+ else
89
+ @domain_suffix
90
+ end
91
+ end
92
+
93
+ # Determine if entry is managed by this utility or not
94
+ def valid_entry?(element)
95
+ # check it's a managed host first
96
+ if element.respond_to?(:comment) && element.comment.to_s.start_with?(HostManager.comment_prefix)
97
+ # check we have hostname and ip
98
+ if element.respond_to?(:name) && element.respond_to?(:address)
99
+ return true
100
+ end
101
+ end
102
+ end
103
+
104
+ def get_hosts
105
+ unless @host_manager
106
+ @host_manager = Hosts::File.read(HostManager.host_file_path)
107
+ end
108
+
109
+ @host_manager
110
+ end
111
+
112
+ def build_hosts_data(container_data)
113
+ data = []
114
+
115
+ if container_data && container_data.is_a?(Array)
116
+ container_data.each do |container_info|
117
+ container = Docker::Container.get(container_info.info['id'])
118
+
119
+ if container.info['State']['Running']
120
+ name = container.info['Name']
121
+
122
+ if name
123
+ ip = container.info['NetworkSettings']['IPAddress']
124
+
125
+ if ip
126
+ compose_host_names(container).each do |hostname|
127
+ item = {
128
+ :hostname => hostname,
129
+ :ip => ip,
130
+ :containerId => container.info['id']
131
+ }
132
+
133
+ data.push(item)
134
+ end
135
+ else
136
+ log.warn("Ignored request to create hosts entry due to no IP being returned from container: #{name}")
137
+ end
138
+ end
139
+ end
140
+
141
+ end
142
+ end
143
+
144
+ data
145
+ end
146
+
147
+ def update_hosts_file(data)
148
+ if data.is_a?(Hash)
149
+ required_keys = %w(action hostname ip containerId)
150
+ required_keys.each do |key|
151
+ unless data.key?(key)
152
+ raise "Data for key #{key} not provided."
153
+ end
154
+ end
155
+ execute(data['action'], data['hostname'], data['ip'], data['containerId'])
156
+
157
+ elsif data.is_a?(Array)
158
+ hosts = get_hosts
159
+
160
+ # initially remove all entries
161
+ hosts.elements.delete_if do |element|
162
+ if valid_entry?(element)
163
+ log.info 'Removing entry for ' + element.name
164
+ true
165
+ end
166
+ end
167
+
168
+ # add recieved entries
169
+ data.each do |entry|
170
+ add_entry(entry, hosts)
171
+ hosts.write
172
+ end
173
+
174
+ hosts.write
175
+
176
+ end
177
+ end
178
+
179
+ def add_entry(entry, hosts)
180
+ if entry[:ip] && entry[:containerId] && entry[:hostname]
181
+ comment = HostManager.comment_prefix + (entry['containerId'] ? " Container ID: #{entry[:containerId]}" : '')
182
+ log.info "Adding entry: #{entry[:hostname]} => #{entry[:ip]}"
183
+ hosts.elements << Hosts::Entry.new(entry[:ip], entry[:hostname], :comment => comment)
184
+
185
+ else
186
+ log.warn 'Unable to write entry due to missing values [ip,containerId,hostname]: ' + entry.to_json
187
+ end
188
+ end
189
+
190
+ def execute(action, hostname, ip, container_id = nil)
191
+ hosts = get_hosts
192
+
193
+ if action == 'create'
194
+ hosts.elements.each do |element|
195
+ if valid_entry?(element) && hostname == element.name
196
+ raise "Entry already exists! (#{ip} #{hostname})"
197
+ end
198
+ end
199
+
200
+ comment = HostManager.comment_prefix + (container_id ? " Container ID: #{container_id}" : '')
201
+
202
+ hosts.elements << Hosts::Entry.new(ip, hostname, :comment => comment)
203
+ hosts.write
204
+
205
+ elsif action == 'delete'
206
+ hosts.elements.delete_if do |element|
207
+ if valid_entry?(element)
208
+ if hostname == element.name
209
+ log.info 'Removing entry for ' + element.name
210
+ true
211
+ end
212
+ end
213
+ end
214
+
215
+ hosts.write
216
+ else
217
+ raise 'No valid action specified.'
218
+ end
219
+ end
220
+
221
+ private
222
+
223
+ def sanitize_host_value(value)
224
+ value.sub(/^\//, '').gsub(/_/, '-')
225
+ end
226
+
227
+ end
228
+
229
+ class Server < Base
230
+ def run(ip, port)
231
+ HostManager.log.info "Running on host manager service on #{ip}:#{port}"
232
+ @tcp_server = TCPServer.new ip, port
233
+
234
+ loop {
235
+
236
+ client = @tcp_server.accept
237
+ content = client.gets
238
+
239
+ log.debug("Recieved data: #{content.to_s.strip}")
240
+
241
+ begin
242
+ unless content.to_s.chomp.strip.empty?
243
+ data = JSON.parse(content)
244
+ update_hosts_file(data)
245
+ end
246
+
247
+ rescue => e
248
+ log.error("Caught exception attempting to execute with data #{content}. #{e}")
249
+ end
250
+
251
+ client.close
252
+ }
253
+
254
+ end
255
+
256
+ def self.create(ip, port)
257
+ server = self.new
258
+ server.run(ip, port)
259
+ end
260
+ end
261
+
262
+ class Client < Base
263
+
264
+ def initialize(ip, port)
265
+ @socket = TCPSocket.new(ip, port)
266
+ end
267
+
268
+ def close
269
+ log.info('Closing client connection.')
270
+ @socket.close
271
+ end
272
+
273
+ def sync_hosts(container_data)
274
+ data = build_hosts_data(container_data)
275
+
276
+ begin
277
+ update_hosts_file(data)
278
+
279
+ `service dnsmasq restart`
280
+
281
+ rescue => e
282
+ log.error("Caught exception attempting to write to hosts file with data #{data.to_json}. #{e}")
283
+ end
284
+
285
+ send(data)
286
+ end
287
+
288
+ def send(data)
289
+ log.info("Sending data via client: #{data.to_json}")
290
+ @socket.write data.to_json + "\n"
291
+ end
292
+
293
+ def self.create(ip, port)
294
+ self.new(ip, port)
295
+ end
296
+ end
297
+
298
+ end
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+ # vi: set ft=ruby :
4
+ require 'docker'
5
+ require 'yaml'
6
+
7
+
8
+ # build up hash of id => project name
9
+ id_project_hash = {}
10
+ Docker::Container.all(all: true, filters: { status: ["running"] }.to_json).each do |container|
11
+ # puts container.info['id']
12
+ # puts container.info['Labels']['com.docker.compose.project'].inspect
13
+
14
+ id_project_hash[container.info['id']] = container.info['Labels']['com.docker.compose.project']
15
+ # puts container.info['NetworkSettings']['IPAddress']
16
+ # puts container.info.to_yaml
17
+ end
18
+
19
+
20
+ module HostManager
21
+ def self.register(hostname, ip)
22
+ entry = "#{ip} #{hostname}"
23
+ puts entry
24
+ end
25
+ end
26
+
27
+ Docker::Network.all.each do |network|
28
+ # puts network.info['Options'].inspect
29
+ # puts network.inspect
30
+ network.info['Containers'].each do |pair|
31
+ id = pair[0]
32
+ container = pair[1]
33
+ ip = container['IPv4Address'][/[^\/]+/]
34
+ if id_project_hash.key?(id) and id_project_hash[id]
35
+ project_name = id_project_hash[id]
36
+ HostManager.register("#{container['Name']}.#{project_name}.local", ip)
37
+ elsif
38
+ HostManager.register("#{container['Name']}.local", ip)
39
+ end
40
+ end
41
+ end
42
+
43
+ # Docker::Event.stream { |event|
44
+ # # respond to all events
45
+ # puts event.Attributes.name;
46
+ # }
data/lib/server.rb ADDED
@@ -0,0 +1,3 @@
1
+ require_relative 'host-manager'
2
+
3
+ HostManager::Server.create('0.0.0.0', 2991)
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+
3
+ begin
4
+ require 'vagrant'
5
+ rescue LoadError
6
+ Bundler.require(:default, :development)
7
+ end
8
+
9
+ require 'vagrant-dnsdock-hostupdater/plugin'
10
+ require 'vagrant-dnsdock-hostupdater/command'
data/lib/version.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Vagrant
2
+ module DnsdockHostUpdater
3
+ VERSION = '0.0.24'.freeze
4
+ end
5
+ end
@@ -11,9 +11,9 @@ DNSDock should be configured on the guest machine to enable containers to resolv
11
11
  DESCRIPTION
12
12
  s.authors = ['Brian Coit']
13
13
  s.email = 'brian.coit@cellosignal.com'
14
- s.files = Dir['{lib}'] + ['Rakefile', 'Gemfile', 'Gemfile.lock', 'vagrant-dnsdock-hostupdater.gemspec']
14
+ s.files = Dir['{lib}/**'] + ['Rakefile', 'Gemfile', 'Gemfile.lock', 'vagrant-dnsdock-hostupdater.gemspec']
15
15
  s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
- s.require_paths = ['lib/**']
16
+ s.require_paths = ['lib']
17
17
  s.homepage = 'https://bitbucket.org/briancoit'
18
18
  s.license = 'ISC'
19
19
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-dnsdock-hostupdater
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.23
4
+ version: 0.0.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Coit
@@ -64,6 +64,13 @@ files:
64
64
  - Gemfile
65
65
  - Gemfile.lock
66
66
  - Rakefile
67
+ - lib/client.rb
68
+ - lib/event-watcher.rb
69
+ - lib/host-manager.rb
70
+ - lib/launch-control
71
+ - lib/server.rb
72
+ - lib/vagrant-dnsdock-hostupdater.rb
73
+ - lib/version.rb
67
74
  - vagrant-dnsdock-hostupdater.gemspec
68
75
  homepage: https://bitbucket.org/briancoit
69
76
  licenses:
@@ -72,7 +79,7 @@ metadata: {}
72
79
  post_install_message:
73
80
  rdoc_options: []
74
81
  require_paths:
75
- - lib/**
82
+ - lib
76
83
  required_ruby_version: !ruby/object:Gem::Requirement
77
84
  requirements:
78
85
  - - ">="