vagrant-dnsdock-hostupdater 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ Bundler::GemHelper.install_tasks
data/lib/client.rb ADDED
@@ -0,0 +1,5 @@
1
+ require_relative 'host-manager'
2
+
3
+ HostManager::Client
4
+ .create('10.10.10.1', 2991)
5
+ .send_action('delete', 'the1234.test.host', '127.0.0.1', 'containerid')
@@ -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
+
@@ -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
+