vagrant-dnsdock-hostupdater 0.0.10
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 +7 -0
- data/Rakefile +3 -0
- data/lib/client.rb +5 -0
- data/lib/event-watcher.rb +35 -0
- data/lib/host-manager.rb +193 -0
- data/lib/hosts/aef/hosts.rb +51 -0
- data/lib/hosts/aef/hosts/comment.rb +73 -0
- data/lib/hosts/aef/hosts/element.rb +108 -0
- data/lib/hosts/aef/hosts/empty_element.rb +50 -0
- data/lib/hosts/aef/hosts/entry.rb +123 -0
- data/lib/hosts/aef/hosts/file.rb +252 -0
- data/lib/hosts/aef/hosts/helpers.rb +121 -0
- data/lib/hosts/aef/hosts/section.rb +141 -0
- data/lib/hosts/aef/hosts/version.rb +29 -0
- data/lib/hosts/aef/linebreak/linebreak.rb +148 -0
- data/lib/hosts/hosts.rb +25 -0
- data/lib/hosts/hosts/bare.rb +23 -0
- data/lib/launch-control +46 -0
- data/lib/server.rb +3 -0
- data/lib/vagrant-dnsdock-hostupdater.rb +10 -0
- data/lib/vagrant-dnsdock-hostupdater/command.rb +12 -0
- data/lib/vagrant-dnsdock-hostupdater/plugin.rb +91 -0
- data/lib/version.rb +5 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9c1d494bc348ad4bf14e33a49b68878e9089ad25
|
4
|
+
data.tar.gz: f58089650e77004454d1c56d7beec701dc534b96
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 716cdb7352cd498a5636712a6e488e713ee80ef8ba6dacad2a7e23b0c86a8641f1b5da568d73b48260511a80f63821e496a0018e46b59c64f4323b0be8ffc087
|
7
|
+
data.tar.gz: 150e266e511a23b431fced083a0c22d5fcd1659fba4f780146aadfd7aa57abe8ba04110b2f53ae0509322d88325f9599a0a010b1ed34889eee1feb50a190ebfa
|
data/Rakefile
ADDED
data/lib/client.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'host-manager'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
loop {
|
5
|
+
begin
|
6
|
+
Docker::Event.stream { |event|
|
7
|
+
HostManager.log.info 'Connected to docker socket. Listening for events.'
|
8
|
+
# respond to all container events
|
9
|
+
if event.type == 'container'
|
10
|
+
container_id = event.actor.id.chomp
|
11
|
+
|
12
|
+
container = Docker::Container.get(container_id)
|
13
|
+
ip = container.info['NetworkSettings']['IPAddress']
|
14
|
+
container_name = container.info['Name'].sub!(/^\//, '')
|
15
|
+
|
16
|
+
client = HostManager::Client.create('10.10.10.1', 2991)
|
17
|
+
client.host_suffix '.local.signal.sh'
|
18
|
+
|
19
|
+
if event.action == 'start'
|
20
|
+
client.send_action('create', container_name, ip, container_id)
|
21
|
+
elsif event.action == 'die'
|
22
|
+
client.send_action('delete', container_name, ip, container_id)
|
23
|
+
end
|
24
|
+
|
25
|
+
client.close
|
26
|
+
end
|
27
|
+
|
28
|
+
break
|
29
|
+
}
|
30
|
+
|
31
|
+
rescue Docker::Error::TimeoutError
|
32
|
+
HostManager.log.warn 'Docker socket connection timed out whilst listening for events. Reconnecting...'
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
data/lib/host-manager.rb
ADDED
@@ -0,0 +1,193 @@
|
|
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
|
+
container_name + host_suffix
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def host_suffix(suffix = nil)
|
61
|
+
if suffix != nil
|
62
|
+
@domain_suffix = suffix
|
63
|
+
else
|
64
|
+
@domain_suffix
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Server < Base
|
70
|
+
def run(ip, port)
|
71
|
+
HostManager.log.info "Running on host manager service on #{ip}:#{port}"
|
72
|
+
@tcp_server = TCPServer.new ip, port
|
73
|
+
|
74
|
+
loop {
|
75
|
+
|
76
|
+
client = @tcp_server.accept
|
77
|
+
content = client.gets
|
78
|
+
|
79
|
+
log.debug("Recieved data: #{content.to_s.strip}")
|
80
|
+
|
81
|
+
begin
|
82
|
+
unless content.to_s.chomp.strip.empty?
|
83
|
+
data = JSON.parse(content)
|
84
|
+
required_keys = %w(action hostname ip containerId)
|
85
|
+
required_keys.each do |key|
|
86
|
+
unless data.key?(key)
|
87
|
+
raise "Data for key #{key} not provided."
|
88
|
+
end
|
89
|
+
end
|
90
|
+
execute(data['action'], data['hostname'], data['ip'], data['containerId'])
|
91
|
+
end
|
92
|
+
rescue => e
|
93
|
+
log.error("Caught exception attempting to execute with data #{content}. #{e}")
|
94
|
+
end
|
95
|
+
|
96
|
+
client.close
|
97
|
+
}
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_hosts
|
102
|
+
Hosts::File.read(HostManager.host_file_path)
|
103
|
+
end
|
104
|
+
|
105
|
+
def execute(action, hostname, ip, container_id = nil)
|
106
|
+
hosts = get_hosts
|
107
|
+
|
108
|
+
if action == 'create'
|
109
|
+
hosts.elements.each do |element|
|
110
|
+
if valid_entry?(element)
|
111
|
+
if hostname == element.name && ip == element.address
|
112
|
+
raise "Entry already exists! (#{ip} #{hostname})"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
comment = HostManager.comment_prefix + (container_id ? " Container ID: #{container_id}" : '')
|
118
|
+
|
119
|
+
hosts.elements << Hosts::Entry.new(ip, hostname, :comment => comment)
|
120
|
+
hosts.write
|
121
|
+
|
122
|
+
elsif action == 'delete'
|
123
|
+
hosts.elements.delete_if do |element|
|
124
|
+
if valid_entry?(element)
|
125
|
+
if hostname == element.name
|
126
|
+
log.info 'Removing entry for ' + element.name
|
127
|
+
true
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
hosts.write
|
133
|
+
else
|
134
|
+
raise 'No valid action specified.'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.create(ip, port)
|
139
|
+
server = self.new
|
140
|
+
server.run(ip, port)
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
# Determine if entry is managed by this utility or not
|
146
|
+
def valid_entry?(element)
|
147
|
+
# check it's a managed host first
|
148
|
+
if element.respond_to?(:comment) && element.comment.to_s.start_with?(HostManager.comment_prefix)
|
149
|
+
# check we have hostname and ip
|
150
|
+
if element.respond_to?(:name) && element.respond_to?(:address)
|
151
|
+
return true
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
class Client < Base
|
159
|
+
|
160
|
+
def initialize(ip, port)
|
161
|
+
@socket = TCPSocket.new(ip, port)
|
162
|
+
end
|
163
|
+
|
164
|
+
def close
|
165
|
+
log.info('Closing client connection.')
|
166
|
+
@socket.close
|
167
|
+
end
|
168
|
+
|
169
|
+
def send_action(action, hostname, ip, container_id = nil)
|
170
|
+
data = {
|
171
|
+
:action => action,
|
172
|
+
:hostname => host_name(hostname),
|
173
|
+
:ip => ip,
|
174
|
+
}
|
175
|
+
|
176
|
+
if container_id
|
177
|
+
data[:containerId] = container_id
|
178
|
+
end
|
179
|
+
|
180
|
+
send(data)
|
181
|
+
end
|
182
|
+
|
183
|
+
def send(data)
|
184
|
+
log.info("Sending data via client: #{data.to_json}")
|
185
|
+
@socket.write data.to_json + "\n"
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.create(ip, port)
|
189
|
+
self.new(ip, port)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
Copyright Alexander E. Fischer <aef@raxys.net>, 2012
|
4
|
+
|
5
|
+
This file is part of Hosts.
|
6
|
+
|
7
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
8
|
+
purpose with or without fee is hereby granted, provided that the above
|
9
|
+
copyright notice and this permission notice appear in all copies.
|
10
|
+
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
12
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
13
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
14
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
15
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
16
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
17
|
+
PERFORMANCE OF THIS SOFTWARE.
|
18
|
+
=end
|
19
|
+
|
20
|
+
require 'set'
|
21
|
+
require 'pathname'
|
22
|
+
require_relative 'linebreak/linebreak'
|
23
|
+
|
24
|
+
# Namespace for projects of Alexander E. Fischer <aef@raxys.net>.
|
25
|
+
#
|
26
|
+
# If you want to be able to simply type Example instead of Aef::Example to
|
27
|
+
# address classes in this namespace simply write the following before using the
|
28
|
+
# classes.
|
29
|
+
#
|
30
|
+
# @example Including the namespace
|
31
|
+
# include Aef
|
32
|
+
# @author Alexander E. Fischer
|
33
|
+
module Aef
|
34
|
+
|
35
|
+
# Namespace for the hosts library
|
36
|
+
module Hosts
|
37
|
+
|
38
|
+
# An exception for errors happening while parsing a hosts file
|
39
|
+
class ParserError < RuntimeError; end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
require_relative 'hosts/version'
|
44
|
+
require_relative 'hosts/helpers'
|
45
|
+
require_relative 'hosts/file'
|
46
|
+
require_relative 'hosts/element'
|
47
|
+
require_relative 'hosts/section'
|
48
|
+
require_relative 'hosts/entry'
|
49
|
+
require_relative 'hosts/comment'
|
50
|
+
require_relative 'hosts/empty_element'
|
51
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
Copyright Alexander E. Fischer <aef@raxys.net>, 2012
|
4
|
+
|
5
|
+
This file is part of Hosts.
|
6
|
+
|
7
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
8
|
+
purpose with or without fee is hereby granted, provided that the above
|
9
|
+
copyright notice and this permission notice appear in all copies.
|
10
|
+
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
12
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
13
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
14
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
15
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
16
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
17
|
+
PERFORMANCE OF THIS SOFTWARE.
|
18
|
+
=end
|
19
|
+
|
20
|
+
require_relative '../hosts'
|
21
|
+
|
22
|
+
module Aef
|
23
|
+
module Hosts
|
24
|
+
|
25
|
+
# Represents a comment-only line as element of a hosts file
|
26
|
+
class Comment < Element
|
27
|
+
|
28
|
+
# The comment
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
attr_reader :comment
|
32
|
+
|
33
|
+
# Initializes a comment
|
34
|
+
#
|
35
|
+
# @param comment [String] the comment
|
36
|
+
# @param options [Hash]
|
37
|
+
# @option options [String] :cache sets a cached String representation
|
38
|
+
def initialize(comment, options = {})
|
39
|
+
validate_options(options, :cache)
|
40
|
+
|
41
|
+
raise ArgumentError, 'Comment cannot be empty' unless comment
|
42
|
+
|
43
|
+
@comment = comment.to_s
|
44
|
+
@cache = options[:cache].to_s unless options[:cache].nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sets the comment
|
48
|
+
def comment=(comment)
|
49
|
+
set_if_changed(:comment, comment.to_s) do
|
50
|
+
invalidate_cache!
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# A String representation for debugging purposes
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
def inspect
|
58
|
+
generate_inspect(self, :comment, :cache)
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
# Defines the algorithm to generate a String representation from scratch.
|
64
|
+
#
|
65
|
+
# @return [String] a generated String representation
|
66
|
+
def generate_string(options = {})
|
67
|
+
"##{comment}\n"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
Copyright Alexander E. Fischer <aef@raxys.net>, 2012
|
4
|
+
|
5
|
+
This file is part of Hosts.
|
6
|
+
|
7
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
8
|
+
purpose with or without fee is hereby granted, provided that the above
|
9
|
+
copyright notice and this permission notice appear in all copies.
|
10
|
+
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
12
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
13
|
+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
14
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
15
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
16
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
17
|
+
PERFORMANCE OF THIS SOFTWARE.
|
18
|
+
=end
|
19
|
+
|
20
|
+
require_relative '../hosts'
|
21
|
+
|
22
|
+
module Aef
|
23
|
+
module Hosts
|
24
|
+
|
25
|
+
# The base class for elements which are aggregated by the Aef::Hosts::File
|
26
|
+
# class.
|
27
|
+
#
|
28
|
+
# @abstract This class is not supposed to be instantiated.
|
29
|
+
class Element
|
30
|
+
|
31
|
+
include Helpers
|
32
|
+
|
33
|
+
# Cached String representation
|
34
|
+
#
|
35
|
+
# @return [String, Hash, nil]
|
36
|
+
attr_reader :cache
|
37
|
+
|
38
|
+
# Deletes the cached String representation
|
39
|
+
#
|
40
|
+
# @return [Aef::Hosts::Element] a self reference
|
41
|
+
def invalidate_cache!
|
42
|
+
@cache = nil
|
43
|
+
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Tells if a String representation is cached or not
|
48
|
+
#
|
49
|
+
# @return [true, false] true if cache is not empty
|
50
|
+
def cache_filled?
|
51
|
+
@cache ? true : false
|
52
|
+
end
|
53
|
+
|
54
|
+
# A String representation for debugging purposes
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
def inspect
|
58
|
+
generate_inspect(self, :cache)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Provides a String representation of the element
|
62
|
+
#
|
63
|
+
# @param [Hash] options
|
64
|
+
# @option options [true, false] :force_generation if set to true, the
|
65
|
+
# cache won't be used, even if it not empty
|
66
|
+
# @option options [:unix, :windows, :mac] :linebreak_encoding the
|
67
|
+
# linebreak encoding of the result. If nothing is specified the result
|
68
|
+
# will be encoded as if :unix was specified.
|
69
|
+
# @see Aef::Linebreak#encode
|
70
|
+
def to_s(options = {})
|
71
|
+
validate_options(options, :force_generation, :linebreak_encoding)
|
72
|
+
|
73
|
+
string = ''
|
74
|
+
|
75
|
+
if !cache_filled? || options[:force_generation]
|
76
|
+
string << generate_string(options)
|
77
|
+
else
|
78
|
+
string << cache_string(options)
|
79
|
+
end
|
80
|
+
|
81
|
+
if options[:linebreak_encoding]
|
82
|
+
string = Aef::Linebreak.encode(string, options[:linebreak_encoding])
|
83
|
+
end
|
84
|
+
|
85
|
+
string
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
# Defines the algorithm to generate a String representation from scratch.
|
91
|
+
#
|
92
|
+
# @abstract This method needs to be implemented in descendant classes.
|
93
|
+
# @return [String] a generated String representation
|
94
|
+
def generate_string(options = {})
|
95
|
+
raise NotImplementedError
|
96
|
+
end
|
97
|
+
|
98
|
+
# Defines the algorithm to construct the String representation from cache
|
99
|
+
#
|
100
|
+
# @return [String] the cached String representation
|
101
|
+
def cache_string(options = {})
|
102
|
+
@cache.dup
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|