hostsfile 0.0.1
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/.gitignore +24 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/Guardfile +17 -0
- data/LICENSE.txt +13 -0
- data/README.md +29 -0
- data/Rakefile +12 -0
- data/hostsfile.gemspec +31 -0
- data/lib/hostsfile/entry.rb +175 -0
- data/lib/hostsfile/manipulator.rb +259 -0
- data/lib/hostsfile/version.rb +5 -0
- data/lib/hostsfile.rb +6 -0
- data/spec/hostsfile_spec.rb +75 -0
- data/spec/spec_helper.rb +4 -0
- metadata +145 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b51455b82fe75732e2b22f6a9222a51abb16b1fa
|
4
|
+
data.tar.gz: 183d81cc05822093b1d3094f3f523da5a98b7a2e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 58ff7a872890643f04307497a6de91f8929890b67fbd3dca1c3d6f828cce7453b0e73c7f430fdd107af29aeb65269d4a079561f7f3046940901d0d4c4d3c1592
|
7
|
+
data.tar.gz: f307fe72a5555b58e10dc8c77fc1f2e77b68387763a165401830d13b3b653d35856a5d712f7a51110ee4e3ee137029a316fb1653956ed9eef3f05604f06cb4d8
|
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.ruby-gemset
|
24
|
+
.ruby-version
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
5
|
+
# rspec may be run, below are examples of the most common uses.
|
6
|
+
# * bundler: 'bundle exec rspec'
|
7
|
+
# * bundler binstubs: 'bin/rspec'
|
8
|
+
# * spring: 'bin/rsspec' (This will use spring if running and you have
|
9
|
+
# installed the spring binstubs per the docs)
|
10
|
+
# * zeus: 'zeus rspec' (requires the server to be started separetly)
|
11
|
+
# * 'just' rspec: 'rspec'
|
12
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
13
|
+
watch(%r{^spec/.+_spec\.rb$})
|
14
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
15
|
+
watch('spec/spec_helper.rb') { "spec" }
|
16
|
+
end
|
17
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2013-14 tnarik <tnarik@lecafeautomatique.co.uk>
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Hostsfile
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'hostsfile'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install hostsfile
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( https://github.com/tnarik/hostsfile/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
# Default :spec task
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
# .rspec is used for options.
|
7
|
+
# Different options for 'rake spec' can be configured using the below version.
|
8
|
+
#RSpec::Core::RakeTask.new do |task|
|
9
|
+
# task.rspec_opts = ['--color', '--format', 'progress']
|
10
|
+
#end
|
11
|
+
|
12
|
+
task :default => :spec
|
data/hostsfile.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hostsfile/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "hostsfile"
|
8
|
+
spec.version = Hostsfile::VERSION
|
9
|
+
spec.authors = ["tnarik"]
|
10
|
+
spec.email = ["tnarik@lecafeautomatique.co.uk"]
|
11
|
+
spec.summary = %q{code from the hostsfile cookbook to allow reusability}
|
12
|
+
spec.description = %q{code from the hostsfile cookbook to allow reusability}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "Apache-2.0"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
25
|
+
spec.add_development_dependency "guard"
|
26
|
+
spec.add_development_dependency "guard-rspec"
|
27
|
+
|
28
|
+
spec.add_development_dependency "terminal-notifier-guard"
|
29
|
+
# Pre OS X 10.8
|
30
|
+
# spec.add_development_dependency "ruby_gntp"
|
31
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# Copyright 2012-2013, Seth Vargo (from customink-webops/hostsfile/libraries/entry.rb)
|
2
|
+
# Copyright 2012, CustomInk, LCC
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'ipaddr'
|
6
|
+
|
7
|
+
module Hostsfile
|
8
|
+
|
9
|
+
# An object representation of a single line in a hostsfile.
|
10
|
+
#
|
11
|
+
# @author Seth Vargo <sethvargo@gmail.com>
|
12
|
+
class Entry
|
13
|
+
class << self
|
14
|
+
# Creates a new Hostsfile::Entry object by parsing a text line. The
|
15
|
+
# `line` attribute will be in the following format:
|
16
|
+
#
|
17
|
+
# 1.2.3.4 hostname [alias[, alias[, alias]]] [# comment [@priority]]
|
18
|
+
#
|
19
|
+
# @param [String] line
|
20
|
+
# the line to parse
|
21
|
+
# @return [Entry]
|
22
|
+
# a new entry object
|
23
|
+
def parse(line)
|
24
|
+
entry, comment = extract_comment(line)
|
25
|
+
comment, priority = extract_priority(comment)
|
26
|
+
entries = extract_entries(entry)
|
27
|
+
|
28
|
+
# Return nil if the line is empty
|
29
|
+
return nil if entries.nil? || entries.empty?
|
30
|
+
|
31
|
+
return self.new(
|
32
|
+
ip_address: entries[0],
|
33
|
+
hostname: entries[1],
|
34
|
+
aliases: entries[2..-1],
|
35
|
+
comment: comment,
|
36
|
+
priority: priority,
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def extract_comment(line)
|
42
|
+
return nil if presence(line).nil?
|
43
|
+
line.split('#', 2).collect { |part| presence(part) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def extract_priority(comment)
|
47
|
+
return nil if comment.nil?
|
48
|
+
|
49
|
+
if comment.include?('@')
|
50
|
+
comment.split('@', 2).collect { |part| presence(part) }
|
51
|
+
else
|
52
|
+
[comment, nil]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def extract_entries(entry)
|
57
|
+
return nil if entry.nil?
|
58
|
+
entry.split(/\s+/).collect { |entry| presence(entry) }.compact
|
59
|
+
end
|
60
|
+
|
61
|
+
def presence(string)
|
62
|
+
return nil if string.nil?
|
63
|
+
return nil if string.strip.empty?
|
64
|
+
string.strip
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [String]
|
69
|
+
attr_accessor :ip_address, :hostname, :aliases, :comment, :priority
|
70
|
+
|
71
|
+
# Creates a new entry from the given options.
|
72
|
+
#
|
73
|
+
# @param [Hash] options
|
74
|
+
# a list of options to create the entry with
|
75
|
+
# @option options [String] :ip_address
|
76
|
+
# the IP Address for this entry
|
77
|
+
# @option options [String] :hostname
|
78
|
+
# the hostname for this entry
|
79
|
+
# @option options [String, Array<String>] :aliases
|
80
|
+
# a alias or array of aliases for this entry
|
81
|
+
# @option options[String] :comment
|
82
|
+
# an optional comment for this entry
|
83
|
+
# @option options [Fixnum] :priority
|
84
|
+
# the relative priority of this entry (compared to others)
|
85
|
+
#
|
86
|
+
# @raise [ArgumentError]
|
87
|
+
# if neither :ip_address nor :hostname are supplied
|
88
|
+
def initialize(options = {})
|
89
|
+
if options[:ip_address].nil? || options[:hostname].nil?
|
90
|
+
raise ArgumentError, ':ip_address and :hostname are both required options'
|
91
|
+
end
|
92
|
+
|
93
|
+
@ip_address = IPAddr.new(options[:ip_address].to_s)
|
94
|
+
@hostname = options[:hostname]
|
95
|
+
@aliases = [options[:aliases]].flatten.compact
|
96
|
+
@comment = options[:comment]
|
97
|
+
@priority = options[:priority] || calculated_priority
|
98
|
+
end
|
99
|
+
|
100
|
+
# Set a the new priority for an entry.
|
101
|
+
#
|
102
|
+
# @param [Fixnum] new_priority
|
103
|
+
# the new priority to set
|
104
|
+
def priority=(new_priority)
|
105
|
+
@calculated_priority = false
|
106
|
+
@priority = new_priority
|
107
|
+
end
|
108
|
+
|
109
|
+
# The line representation of this entry.
|
110
|
+
#
|
111
|
+
# @return [String]
|
112
|
+
# the string representation of this entry
|
113
|
+
def to_line
|
114
|
+
hosts = [hostname, aliases].flatten.join(' ')
|
115
|
+
|
116
|
+
comments = "# #{comment.to_s}".strip
|
117
|
+
comments << " @#{priority}" unless priority.nil? || @calculated_priority
|
118
|
+
comments = comments.strip
|
119
|
+
comments = nil if comments == '#'
|
120
|
+
|
121
|
+
[ip_address, hosts, comments].compact.join("\t").strip
|
122
|
+
end
|
123
|
+
|
124
|
+
# The string representation of this Entry
|
125
|
+
#
|
126
|
+
# @return [String]
|
127
|
+
# the string representation of this entry
|
128
|
+
def to_s
|
129
|
+
"#<#{self.class.to_s} " + [
|
130
|
+
"ip_address: '#{ip_address}'",
|
131
|
+
"hostname: '#{hostname}'",
|
132
|
+
].join(', ') + '>'
|
133
|
+
end
|
134
|
+
|
135
|
+
# The object representation of this Entry
|
136
|
+
#
|
137
|
+
# @return [String]
|
138
|
+
# the object representation of this entry
|
139
|
+
def inspect
|
140
|
+
"#<#{self.class.to_s} " + [
|
141
|
+
"ip_address: '#{ip_address}'",
|
142
|
+
"hostname: '#{hostname}'",
|
143
|
+
"aliases: #{aliases.inspect}",
|
144
|
+
"comment: '#{comment}'",
|
145
|
+
"priority: #{priority}",
|
146
|
+
"calculated_priority?: #{@calculated_priority}",
|
147
|
+
].join(', ') + '>'
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns true if priority is calculated
|
151
|
+
#
|
152
|
+
# @return [Boolean]
|
153
|
+
# true if priority is calculated and false otherwise
|
154
|
+
def calculated_priority?
|
155
|
+
@calculated_priority
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
# Calculates the relative priority of this entry.
|
161
|
+
#
|
162
|
+
# @return [Fixnum]
|
163
|
+
# the relative priority of this item
|
164
|
+
def calculated_priority
|
165
|
+
@calculated_priority = true
|
166
|
+
|
167
|
+
return 81 if ip_address == IPAddr.new('127.0.0.1')
|
168
|
+
return 80 if IPAddr.new('127.0.0.0/8').include?(ip_address) # local
|
169
|
+
return 60 if ip_address.ipv4? # ipv4
|
170
|
+
return 20 if ip_address.ipv6? # ipv6
|
171
|
+
return 00
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
# Copyright 2013-14, Tnarik Innael
|
2
|
+
# Copyright 2012-2013, Seth Vargo (from customink-webops/hostsfile/libraries/manipulator.rb)
|
3
|
+
# Copyright 2012, CustomInk, LCC
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'digest/sha2'
|
7
|
+
|
8
|
+
module Hostsfile
|
9
|
+
|
10
|
+
class Manipulator
|
11
|
+
attr_reader :entries
|
12
|
+
|
13
|
+
# Create a new Manipulator object (aka an /etc/hosts manipulator). If a
|
14
|
+
# hostsfile is not found, a Exception is risen, causing
|
15
|
+
# the process to terminate on the node and the converge will fail.
|
16
|
+
#
|
17
|
+
# @param [Chef::node] node
|
18
|
+
# the current Chef node
|
19
|
+
# @return [Manipulator]
|
20
|
+
# a class designed to manipulate the node's /etc/hosts file
|
21
|
+
def initialize(path = nil, family = nil, system_directory = nil)
|
22
|
+
# Fail if no hostsfile is found
|
23
|
+
unless ::File.exists?(hostsfile_path)
|
24
|
+
raise "No hostsfile exists at '#{hostsfile_path}'!"
|
25
|
+
end
|
26
|
+
|
27
|
+
@entries = []
|
28
|
+
collect_and_flatten(::File.readlines(hostsfile_path))
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return a list of all IP Addresses for this hostsfile.
|
32
|
+
#
|
33
|
+
# @return [Array<IPAddr>]
|
34
|
+
# the list of IP Addresses
|
35
|
+
def ip_addresses
|
36
|
+
@entries.collect do |entry|
|
37
|
+
entry.ip_address
|
38
|
+
end.compact || []
|
39
|
+
end
|
40
|
+
|
41
|
+
# Add a new record to the hostsfile.
|
42
|
+
#
|
43
|
+
# @param [Hash] options
|
44
|
+
# a list of options to create the entry with
|
45
|
+
# @option options [String] :ip_address
|
46
|
+
# the IP Address for this entry
|
47
|
+
# @option options [String] :hostname
|
48
|
+
# the hostname for this entry
|
49
|
+
# @option options [String, Array<String>] :aliases
|
50
|
+
# a alias or array of aliases for this entry
|
51
|
+
# @option options[String] :comment
|
52
|
+
# an optional comment for this entry
|
53
|
+
# @option options [Fixnum] :priority
|
54
|
+
# the relative priority of this entry (compared to others)
|
55
|
+
def add(options = {})
|
56
|
+
entry = ::Hostsfile::Entry.new(
|
57
|
+
ip_address: options[:ip_address],
|
58
|
+
hostname: options[:hostname],
|
59
|
+
aliases: options[:aliases],
|
60
|
+
comment: options[:comment],
|
61
|
+
priority: options[:priority],
|
62
|
+
)
|
63
|
+
|
64
|
+
@entries << entry
|
65
|
+
remove_existing_hostnames(entry) if options[:unique]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Update an existing entry. This method will do nothing if the entry
|
69
|
+
# does not exist.
|
70
|
+
#
|
71
|
+
# @param (see #add)
|
72
|
+
def update(options = {})
|
73
|
+
if entry = find_entry_by_ip_address(options[:ip_address])
|
74
|
+
entry.hostname = options[:hostname]
|
75
|
+
entry.aliases = options[:aliases]
|
76
|
+
entry.comment = options[:comment]
|
77
|
+
entry.priority = options[:priority]
|
78
|
+
|
79
|
+
remove_existing_hostnames(entry) if options[:unique]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Append content to an existing entry. This method will add a new entry
|
84
|
+
# if one does not already exist.
|
85
|
+
#
|
86
|
+
# @param (see #add)
|
87
|
+
def append(options = {})
|
88
|
+
if entry = find_entry_by_ip_address(options[:ip_address])
|
89
|
+
hosts = normalize(entry.hostname, entry.aliases, options[:hostname], options[:aliases])
|
90
|
+
entry.hostname = hosts.shift
|
91
|
+
entry.aliases = hosts
|
92
|
+
|
93
|
+
unless entry.comment && options[:comment] && entry.comment.include?(options[:comment])
|
94
|
+
entry.comment = normalize(entry.comment, options[:comment]).join(', ')
|
95
|
+
end
|
96
|
+
|
97
|
+
remove_existing_hostnames(entry) if options[:unique]
|
98
|
+
else
|
99
|
+
add(options)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Remove an entry by it's IP Address
|
104
|
+
#
|
105
|
+
# @param [String] ip_address
|
106
|
+
# the IP Address of the entry to remove
|
107
|
+
def remove(ip_address)
|
108
|
+
if entry = find_entry_by_ip_address(ip_address)
|
109
|
+
@entries.delete(entry)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Save the new hostsfile to the target machine. This method will only write the
|
114
|
+
# hostsfile if the current version has changed. In other words, it is convergent.
|
115
|
+
def save
|
116
|
+
entries = []
|
117
|
+
entries << '#'
|
118
|
+
entries << '# This file is managed by Chef, using the hostsfile cookbook.'
|
119
|
+
entries << '# Editing this file by hand is highly discouraged!'
|
120
|
+
entries << '#'
|
121
|
+
entries << '# Comments containing an @ sign should not be modified or else'
|
122
|
+
entries << '# hostsfile will be unable to guarantee relative priority in'
|
123
|
+
entries << '# future Chef runs!'
|
124
|
+
entries << '#'
|
125
|
+
entries << ''
|
126
|
+
entries += unique_entries.map(&:to_line)
|
127
|
+
entries << ''
|
128
|
+
|
129
|
+
contents = entries.join("\n")
|
130
|
+
contents_sha = Digest::SHA512.hexdigest(contents)
|
131
|
+
|
132
|
+
# Only write out the file if the contents have changed...
|
133
|
+
if contents_sha != current_sha
|
134
|
+
::File.open(hostsfile_path, 'w') do |f|
|
135
|
+
f.write(contents)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Find an entry by the given IP Address.
|
141
|
+
#
|
142
|
+
# @param [String] ip_address
|
143
|
+
# the IP Address of the entry to find
|
144
|
+
# @return [Entry, nil]
|
145
|
+
# the corresponding entry object, or nil if it does not exist
|
146
|
+
def find_entry_by_ip_address(ip_address)
|
147
|
+
@entries.find do |entry|
|
148
|
+
!entry.ip_address.nil? && entry.ip_address == ip_address
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Determine if the current hostsfile contains the given resource. This
|
153
|
+
# is really just a proxy to {find_resource_by_ip_address} /
|
154
|
+
#
|
155
|
+
# @param [Chef::Resource] resource
|
156
|
+
#
|
157
|
+
# @return [Boolean]
|
158
|
+
def contains?(resource)
|
159
|
+
!!find_entry_by_ip_address(resource.ip_address)
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
# The path to the current hostsfile.
|
164
|
+
#
|
165
|
+
# @return [String]
|
166
|
+
# the full path to the hostsfile, depending on the operating system
|
167
|
+
# can also be overriden in the attributes
|
168
|
+
def hostsfile_path (path = nil, family = nil, system_directory = nil)
|
169
|
+
return @hostsfile_path if @hostsfile_path
|
170
|
+
@hostsfile_path = path || case family
|
171
|
+
when 'windows'
|
172
|
+
"#{system_directory}\\drivers\\etc\\hosts"
|
173
|
+
else
|
174
|
+
'/etc/hosts'
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# The current sha of the system hostsfile.
|
179
|
+
#
|
180
|
+
# @return [String]
|
181
|
+
# the sha of the current hostsfile
|
182
|
+
def current_sha
|
183
|
+
@current_sha ||= Digest::SHA512.hexdigest(File.read(hostsfile_path))
|
184
|
+
end
|
185
|
+
|
186
|
+
# Normalize the given list of elements into a single array with no nil
|
187
|
+
# values and no duplicate values.
|
188
|
+
#
|
189
|
+
# @param [Object] things
|
190
|
+
#
|
191
|
+
# @return [Array]
|
192
|
+
# a normalized array of things
|
193
|
+
def normalize(*things)
|
194
|
+
things.flatten.compact.uniq
|
195
|
+
end
|
196
|
+
|
197
|
+
# This is a crazy way of ensuring unique objects in an array using a Hash.
|
198
|
+
#
|
199
|
+
# @return [Array]
|
200
|
+
# the sorted list of entires that are unique
|
201
|
+
def unique_entries
|
202
|
+
entries = Hash[*@entries.map { |entry| [entry.ip_address, entry] }.flatten].values
|
203
|
+
entries.sort_by { |e| [-e.priority.to_i, e.hostname.to_s] }
|
204
|
+
end
|
205
|
+
|
206
|
+
# Takes /etc/hosts file contents and builds a flattened entries
|
207
|
+
# array so that each IP address has only one line and multiple hostnames
|
208
|
+
# are flattened into a list of aliases.
|
209
|
+
#
|
210
|
+
# @param [Array] contents
|
211
|
+
# Array of lines from /etc/hosts file
|
212
|
+
def collect_and_flatten(contents)
|
213
|
+
contents.each do |line|
|
214
|
+
entry = ::Hostsfile::Entry.parse(line)
|
215
|
+
next if entry.nil?
|
216
|
+
|
217
|
+
append(
|
218
|
+
ip_address: entry.ip_address,
|
219
|
+
hostname: entry.hostname,
|
220
|
+
aliases: entry.aliases,
|
221
|
+
comment: entry.comment,
|
222
|
+
priority: !entry.calculated_priority? && entry.priority,
|
223
|
+
)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Removes duplicate hostnames in other files ensuring they are unique
|
228
|
+
#
|
229
|
+
# @param [Entry] entry
|
230
|
+
# the entry to keep the hostname and aliases from
|
231
|
+
#
|
232
|
+
# @return [nil]
|
233
|
+
def remove_existing_hostnames(entry)
|
234
|
+
@entries.delete(entry)
|
235
|
+
changed_hostnames = [entry.hostname, entry.aliases].flatten.uniq
|
236
|
+
|
237
|
+
@entries = @entries.collect do |entry|
|
238
|
+
entry.hostname = nil if changed_hostnames.include?(entry.hostname)
|
239
|
+
entry.aliases = entry.aliases - changed_hostnames
|
240
|
+
|
241
|
+
if entry.hostname.nil?
|
242
|
+
if entry.aliases.empty?
|
243
|
+
nil
|
244
|
+
else
|
245
|
+
entry.hostname = entry.aliases.shift
|
246
|
+
entry
|
247
|
+
end
|
248
|
+
else
|
249
|
+
entry
|
250
|
+
end
|
251
|
+
end.compact
|
252
|
+
|
253
|
+
@entries << entry
|
254
|
+
|
255
|
+
nil
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
data/lib/hostsfile.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
describe Hostsfile do
|
2
|
+
describe "::VERSION" do
|
3
|
+
it "exists" do
|
4
|
+
expect(Hostsfile::VERSION).not_to be_empty
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "::Entry" do
|
9
|
+
it "exists" do
|
10
|
+
expect(Hostsfile::Entry::respond_to? :new).to eq(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "#to_line" do
|
14
|
+
h = Hostsfile::Entry.new ip_address: "0.0.0.0", hostname: "test"
|
15
|
+
expect(h.respond_to? :to_line).to eq(true)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "#to_line_response" do
|
19
|
+
h = Hostsfile::Entry.new ip_address: "0.0.0.0", hostname: "test"
|
20
|
+
expect(h.to_line).to eq("0.0.0.0\ttest")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "#to_s" do
|
24
|
+
h = Hostsfile::Entry.new ip_address: "0.0.0.0", hostname: "test"
|
25
|
+
expect(h.respond_to? :to_line).to eq(true)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "::Manipulator" do
|
30
|
+
it "exists" do
|
31
|
+
expect(Hostsfile::Manipulator::respond_to? :new).to eq(true)
|
32
|
+
end
|
33
|
+
|
34
|
+
#it "#append" do
|
35
|
+
# h = Hostsfile::Manipulator.new
|
36
|
+
# expect(h.respond_to? :append).to eq(true)
|
37
|
+
#end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
## Manipulator
|
42
|
+
# def initialize(path = nil, family = nil, system_directory = nil)
|
43
|
+
# def ip_addresses
|
44
|
+
# def add(options = {})
|
45
|
+
# def update(options = {})
|
46
|
+
# def append(options = {})
|
47
|
+
# def remove(ip_address)
|
48
|
+
# def save
|
49
|
+
# def find_entry_by_ip_address(ip_address)
|
50
|
+
# def contains?(resource)
|
51
|
+
# private
|
52
|
+
# def hostsfile_path (path = nil, family = nil, system_directory = nil)
|
53
|
+
# def current_sha
|
54
|
+
# def normalize(*things)
|
55
|
+
# def unique_entries
|
56
|
+
# def collect_and_flatten(contents)
|
57
|
+
# def remove_existing_hostnames(entry)
|
58
|
+
|
59
|
+
|
60
|
+
## Entry
|
61
|
+
# def parse(line)
|
62
|
+
# private
|
63
|
+
# def extract_comment(line)
|
64
|
+
# def extract_priority(comment)
|
65
|
+
# def extract_entries(entry)
|
66
|
+
# def presence(string)
|
67
|
+
#
|
68
|
+
# def initialize(options = {})
|
69
|
+
# def priority=(new_priority)
|
70
|
+
# def to_line
|
71
|
+
# def to_s
|
72
|
+
# def inspect
|
73
|
+
# def calculated_priority?
|
74
|
+
# private
|
75
|
+
# def calculated_priority
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hostsfile
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- tnarik
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: guard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: terminal-notifier-guard
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: code from the hostsfile cookbook to allow reusability
|
98
|
+
email:
|
99
|
+
- tnarik@lecafeautomatique.co.uk
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- ".travis.yml"
|
107
|
+
- Gemfile
|
108
|
+
- Guardfile
|
109
|
+
- LICENSE.txt
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- hostsfile.gemspec
|
113
|
+
- lib/hostsfile.rb
|
114
|
+
- lib/hostsfile/entry.rb
|
115
|
+
- lib/hostsfile/manipulator.rb
|
116
|
+
- lib/hostsfile/version.rb
|
117
|
+
- spec/hostsfile_spec.rb
|
118
|
+
- spec/spec_helper.rb
|
119
|
+
homepage: ''
|
120
|
+
licenses:
|
121
|
+
- Apache-2.0
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.2.2
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: code from the hostsfile cookbook to allow reusability
|
143
|
+
test_files:
|
144
|
+
- spec/hostsfile_spec.rb
|
145
|
+
- spec/spec_helper.rb
|