diff --git a/Gemfile b/Gemfile index a0fa2c8..580ade3 100644 --- a/Gemfile +++ b/Gemfile @@ -2,3 +2,5 @@ source 'https://rubygems.org' # Specify your gem's dependencies in linux_admin.gemspec gemspec + +gem "more_core_extensions", "=1.1.0", :git => "git://github.com/ManageIQ/more_core_extensions.git", :tag => "v1.1.0" diff --git a/lib/linux_admin.rb b/lib/linux_admin.rb index b221d86..9e4ad47 100644 --- a/lib/linux_admin.rb +++ b/lib/linux_admin.rb @@ -12,6 +12,7 @@ require 'linux_admin/service' require 'linux_admin/disk' +require 'linux_admin/hosts' require 'linux_admin/partition' require 'linux_admin/distro' require 'linux_admin/system' diff --git a/lib/linux_admin/hosts.rb b/lib/linux_admin/hosts.rb new file mode 100644 index 0000000..69bfbb3 --- /dev/null +++ b/lib/linux_admin/hosts.rb @@ -0,0 +1,73 @@ +class LinuxAdmin + class Hosts < LinuxAdmin + attr_accessor :filename + attr_accessor :raw_lines + attr_accessor :parsed_file + + def initialize(filename = "/etc/hosts") + @filename = filename + self.reload + end + + def reload + @raw_lines = File.read(@filename).split("\n") + parse_file + end + + def save + cleanup_empty + @raw_lines = assemble_lines + File.write(@filename, @raw_lines.join("\n")) + end + + def update_entry(address, hostname, comment = nil) + # Delete entries for this hostname first + @parsed_file.each {|i| i[:hosts].to_a.delete(hostname)} + + # Add entry + line_number = @parsed_file.find_path(address).first + + if line_number.blank? + @parsed_file.push({:address => address, :hosts => [hostname], :comment => comment}) + else + new_hosts = @parsed_file.fetch_path(line_number, :hosts).to_a.push(hostname) + @parsed_file.store_path(line_number, :hosts, new_hosts) + @parsed_file.store_path(line_number, :comment, comment) if comment + end + end + + private + def parse_file + @parsed_file = [] + @raw_lines.each { |line| @parsed_file.push(parse_line(line.strip)) } + @parsed_file.delete_blank_paths + end + + def parse_line(line) + data, comment = line.split("#", 2) + address, hosts = data.to_s.split(" ", 2) + hostnames = hosts.to_s.split(" ") + + { :address => address.to_s, :hosts => hostnames, :comment => comment.to_s.strip, :blank => line.blank?} + end + + def cleanup_empty + @parsed_file.each do |h| + h.delete(:hosts) if h[:address].blank? + h.delete(:address) if h[:hosts].blank? + end + + @parsed_file.delete_blank_paths + end + + def assemble_lines + @parsed_file.each_with_object([]) { |l, a| a.push(l[:blank] ? "" : build_line(l[:address], l[:hosts], l[:comment])) } + end + + def build_line(address, hosts, comment) + line = [address.to_s.ljust(16), hosts.to_a.uniq] + line.push("##{comment}") if comment + line.flatten.join(" ").strip + end + end +end \ No newline at end of file diff --git a/linux_admin.gemspec b/linux_admin.gemspec index 1940e54..23b3c71 100644 --- a/linux_admin.gemspec +++ b/linux_admin.gemspec @@ -33,6 +33,6 @@ registration, updates, etc. spec.add_dependency "activesupport", "< 4.0" spec.add_dependency "inifile", "~> 2.0.2" - spec.add_dependency "more_core_extensions" + spec.add_dependency "more_core_extensions", "~> 1.1.0" spec.add_dependency "nokogiri" end diff --git a/spec/hosts_spec.rb b/spec/hosts_spec.rb new file mode 100644 index 0000000..8efcb9f --- /dev/null +++ b/spec/hosts_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe LinuxAdmin::Hosts do + etc_hosts = "\n #Some Comment\n127.0.0.1\tlocalhost localhost.localdomain # with a comment\n127.0.1.1 my.domain.local" + before do + File.stub(:read).and_return(etc_hosts) + @instance = LinuxAdmin::Hosts.new + end + + describe "#reload" do + it "sets raw_lines" do + expected_array = ["", " #Some Comment", "127.0.0.1\tlocalhost localhost.localdomain # with a comment", "127.0.1.1 my.domain.local"] + expect(@instance.raw_lines).to eq(expected_array) + end + + it "sets parsed_file" do + expected_hash = [{:blank=>true}, {:comment=>"Some Comment"}, {:address=>"127.0.0.1", :hosts=>["localhost", "localhost.localdomain"], :comment=>"with a comment"}, {:address=>"127.0.1.1", :hosts=>["my.domain.local"]}] + expect(@instance.parsed_file).to eq(expected_hash) + end + end + + describe "#update_entry" do + it "removes an existing entry and creates a new one" do + expected_hash = [{:blank=>true}, {:comment=>"Some Comment"}, {:address=>"127.0.0.1", :hosts=>["localhost", "localhost.localdomain"], :comment=>"with a comment"}, {:address=>"127.0.1.1", :hosts=>[]}, {:address=>"1.2.3.4", :hosts=>["my.domain.local"], :comment=>nil}] + @instance.update_entry("1.2.3.4", "my.domain.local") + expect(@instance.parsed_file).to eq(expected_hash) + end + + it "updates an existing entry" do + expected_hash = [{:blank=>true}, {:comment=>"Some Comment"}, {:address=>"127.0.0.1", :hosts=>["localhost", "localhost.localdomain", "new.domain.local"], :comment=>"with a comment"}, {:address=>"127.0.1.1", :hosts=>["my.domain.local"]}] + @instance.update_entry("127.0.0.1", "new.domain.local") + expect(@instance.parsed_file).to eq(expected_hash) + end + end + + describe "#save" do + before do + File.stub(:write) + end + + it "properly generates file with new content" do + expected_array = ["", "#Some Comment", "127.0.0.1 localhost localhost.localdomain #with a comment", "127.0.1.1 my.domain.local", "1.2.3.4 test"] + @instance.update_entry("1.2.3.4", "test") + @instance.save + expect(@instance.raw_lines).to eq(expected_array) + end + + it "properly generates file with removed content" do + expected_array = ["", "#Some Comment", "127.0.0.1 localhost localhost.localdomain my.domain.local #with a comment"] + @instance.update_entry("127.0.0.1", "my.domain.local") + @instance.save + expect(@instance.raw_lines).to eq(expected_array) + end + end +end \ No newline at end of file