Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/linux_admin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
require 'linux_admin/disk'
require 'linux_admin/hosts'
require 'linux_admin/partition'
require 'linux_admin/etc_issue'
require 'linux_admin/distro'
require 'linux_admin/system'
require 'linux_admin/fstab'
Expand Down
2 changes: 1 addition & 1 deletion lib/linux_admin/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
class LinuxAdmin
module Common
def cmd(cmd)
Distro.local.class::COMMANDS[cmd]
Distros::Distro.local.class::COMMANDS[cmd]
end

def run(cmd, options = {})
Expand Down
107 changes: 79 additions & 28 deletions lib/linux_admin/distro.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,9 @@
# Copyright (C) 2013 Red Hat Inc.
# Licensed under the MIT License

class LinuxAdmin
class Distro < LinuxAdmin
attr_accessor :id

def initialize(id)
@id = id
end

def self.local
@local ||= begin
if File.exists?('/etc/issue')
issue = File.read('/etc/issue')
if issue.include?('ubuntu')
return Distros.ubuntu
elsif ['fedora', 'red hat', 'centos'].any? { |d| issue.downcase.include?(d) }
return Distros.redhat
end

elsif File.exists?('/etc/redhat-release') ||
File.exists?('/etc/fedora-release')
return Distros.redhat
end

Distros.generic
end
end

end
require 'linux_admin/etc_issue'

class LinuxAdmin
module Distros
def self.generic
@generic ||= Generic.new
Expand All @@ -41,6 +15,14 @@ def self.redhat
@redhat ||= RedHat.new
end

def self.rhel
@rhel ||= RHEL.new
end

def self.fedora
@fedora ||= Fedora.new
end

def self.ubuntu
@ubuntu ||= Ubuntu.new
end
Expand All @@ -49,6 +31,49 @@ def self.all
@distros ||= [generic, redhat, ubuntu]
end

def self.local
Distro.local
end

class Distro
RELEASE_FILE = ''
ETC_ISSUE_KEYWORDS = []

def self.etc_issue_keywords
self::ETC_ISSUE_KEYWORDS
end

def self.release_file
self::RELEASE_FILE
end

def self.local
# this can be cleaned up..
@local ||= begin
result = nil
Distros.constants.each do |cdistro|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be detect or find?
We should return the first class that is detected?, right? Or return the generic one.

distro_method = cdistro.to_s.downcase.to_sym
distro = Distros.const_get(cdistro)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With possible mixins and stuff in the sub-classes, I'm afraid this method could get extra constants you may not be aware of. Can we use Distro.subclasses?

next unless distro < Distro
result = Distros.send(distro_method) if distro.detected?
end
result || Distros.generic
end
end

def self.detected?
detected_by_etc_issue? || detected_by_etc_release?
end

def self.detected_by_etc_issue?
etc_issue_keywords.any? { |k| EtcIssue.instance.to_s.include?(k) }
end

def self.detected_by_etc_release?
File.exists?(release_file)
end
end

class Generic < Distro
COMMANDS = {}

Expand Down Expand Up @@ -81,7 +106,33 @@ def initialize
end
end

class RHEL < RedHat
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we add/expand these classes, we'll want to split them out into their own files.

RELEASE_FILE = "/etc/redhat-release"
ETC_ISSUE_KEYWORDS = ['red hat', 'Red Hat', 'centos', 'CentOS']

COMMANDS = COMMANDS.merge(
:rpm => '/bin/rpm'
)
def initialize
@id = :rhel
end
end

class Fedora < RedHat
RELEASE_FILE = "/etc/fedora-release"
ETC_ISSUE_KEYWORDS = ['Fedora']

COMMANDS = COMMANDS.merge(
:rpm => '/usr/bin/rpm'
)
def initialize
@id = :fedora
end
end

class Ubuntu < Distro
ETC_ISSUE_KEYWORDS = ['ubuntu']

COMMANDS = {}

def initialize
Expand Down
28 changes: 28 additions & 0 deletions lib/linux_admin/etc_issue.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# LinuxAdmin /etc/issue Representation
#
# Copyright (C) 2014 Red Hat Inc.
# Licensed under the MIT License

require 'singleton'

class LinuxAdmin
class EtcIssue
include Singleton

PATH = '/etc/issue'

def initialize
refresh
end

def to_s
@data.to_s
end

private

def refresh
@data = File.exists?(PATH) ? File.read(PATH) : ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a singleton-based class, then is it possible for this to get out of sync?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fryguy Yes, but this is the OS version file so there's a very high likelihood that you'll need to reboot your linux box when this file changes.

end
end
end
4 changes: 2 additions & 2 deletions lib/linux_admin/package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
class LinuxAdmin
class Package < LinuxAdmin
def self.info(pkg)
if Distro.local == Distros.redhat
if Distros::Distro.local == Distros.redhat
return Rpm.info(pkg)
elsif Distro.local == Distros.ubuntu
elsif Distros::Distro.local == Distros.ubuntu
return Deb.info(pkg)
end

Expand Down
12 changes: 9 additions & 3 deletions lib/linux_admin/rpm.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
class LinuxAdmin
class Rpm < Package
RPM_CMD = '/usr/bin/rpm'
def self.rpm_cmd
Distros::Distro.local.class::COMMANDS[:rpm]
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be handled through the Distro::COMMANDS?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we have the Distro's, I would suggest using them. Also, Fedora and RHEL should be listed as different distro's.


def self.list_installed
out = run!("rpm -qa --qf \"%{NAME} %{VERSION}-%{RELEASE}\n\"").output
out = run!("#{rpm_cmd} -qa --qf \"%{NAME} %{VERSION}-%{RELEASE}\n\"").output
out.split("\n").each_with_object({}) do |line, pkg_hash|
name, ver = line.split(" ")
pkg_hash[name] = ver
Expand All @@ -21,8 +23,12 @@ def self.import_key(file)
def self.info(pkg)
params = { "-qi" => pkg}
in_description = false
out = run!(RPM_CMD, :params => params).output
out = run!(rpm_cmd, :params => params).output
# older versions of rpm may have multiple fields per line,
# split up lines with multiple tags/values:
out.gsub!(/(^.*:.*)\s\s+(.*:.*)$/, "\\1\n\\2")
out.split("\n").each.with_object({}) do |line, rpm|
next if !line || line.empty?
tag,value = line.split(':')
tag = tag.strip
if tag == 'Description'
Expand Down
4 changes: 2 additions & 2 deletions spec/common_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ class TestClass

context "#cmd" do
it "looks up local command from id" do
d = double(LinuxAdmin::Distro)
d = double(LinuxAdmin::Distros::Distro)
d.class::COMMANDS = {:sh => '/bin/sh'}
LinuxAdmin::Distro.should_receive(:local).and_return(d)
LinuxAdmin::Distros::Distro.should_receive(:local).and_return(d)
subject.cmd(:sh).should == '/bin/sh'
end
end
Expand Down
40 changes: 20 additions & 20 deletions spec/distro_spec.rb
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
require 'spec_helper'

describe LinuxAdmin::Distro do
describe LinuxAdmin::Distros::Distro do
describe "#local" do
after(:each) do
# distro generates a local copy, reset after each run
LinuxAdmin::Distro.instance_variable_set(:@local, nil)
end

[['ubuntu', :ubuntu],
['Fedora', :redhat],
['red hat', :redhat],
['CentOS', :redhat],
['centos', :redhat]].each do |i,d|
['Fedora', :fedora],
['red hat', :rhel],
['CentOS', :rhel],
['centos', :rhel]].each do |i, d|
context "/etc/issue contains '#{i}'" do
before(:each) do
File.should_receive(:exists?).with('/etc/issue').and_return(true)
File.should_receive(:read).with('/etc/issue').and_return(i)
LinuxAdmin::EtcIssue.instance.should_receive(:to_s).at_least(:once).and_return(i)
File.should_receive(:exists?).at_least(:once).and_return(false)
end

it "returns Distros.#{d}" do
LinuxAdmin::Distro.local.should == LinuxAdmin::Distros.send(d)
distro = LinuxAdmin::Distros.send(d)
described_class.local.should == distro
end
end
end

context "/etc/issue did not match" do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@movitto I know this isn't in the diff but this context seems wrong. It's really "/etc/issue doesn't exist" right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad. I misread it. I can't think of a better way than stubbing LinuxAdmin::EtcIssue.instance.should_receive(:to_s).at_least(:once).and_return('') below, but it doesn't convey the meaning or intent to me. What do you think? @movitto @brandondunne @Fryguy

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can tackle this when we have better ideas.

before(:each) do
File.should_receive(:exists?).with('/etc/issue').and_return(false)
LinuxAdmin::EtcIssue.instance.should_receive(:to_s).at_least(:once).and_return('')
end

context "/etc/redhat-release exists" do
it "returns Distros.redhat" do
it "returns Distros.rhel" do
File.should_receive(:exists?).with('/etc/redhat-release').and_return(true)
LinuxAdmin::Distro.local.should == LinuxAdmin::Distros.redhat
LinuxAdmin::Distros::Fedora.should_receive(:detected?).and_return(false)
File.should_receive(:exists?).at_least(:once).and_call_original
described_class.local.should == LinuxAdmin::Distros.rhel
end
end

context "/etc/fedora-release exists" do
it "returns Distros.redhat" do
it "returns Distros.fedora" do
File.should_receive(:exists?).with('/etc/redhat-release').and_return(false)
File.should_receive(:exists?).with('/etc/fedora-release').and_return(true)
LinuxAdmin::Distro.local.should == LinuxAdmin::Distros.redhat
File.should_receive(:exists?).at_least(:once).and_call_original
described_class.local.should == LinuxAdmin::Distros.fedora
end
end
end

it "returns Distros.generic" do
File.stub(:exists?).and_return(false)
LinuxAdmin::Distro.local.should == LinuxAdmin::Distros.generic
LinuxAdmin::EtcIssue.instance.should_receive(:to_s).at_least(:once).and_return('')
File.should_receive(:exists?).at_least(:once).and_return(false)
described_class.local.should == LinuxAdmin::Distros.generic
end
end
end
2 changes: 1 addition & 1 deletion spec/logical_volume_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

describe LinuxAdmin::LogicalVolume do
before(:each) do
LinuxAdmin::Distro.stub(:local => LinuxAdmin::Distros::Test.new)
LinuxAdmin::Distros::Distro.stub(:local => LinuxAdmin::Distros::Test.new)

@logical_volumes = <<eos
/dev/vg_foobar/lv_swap:vg_foobar:3:1:-1:2:4128768:63:-1:0:-1:253:0
Expand Down
4 changes: 2 additions & 2 deletions spec/package_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
describe LinuxAdmin::Package do
describe "#info" do
it "dispatches to redhat lookup mechanism" do
LinuxAdmin::Distro.should_receive(:local).and_return(LinuxAdmin::Distros.redhat)
LinuxAdmin::Distros::Distro.should_receive(:local).and_return(LinuxAdmin::Distros.redhat)
LinuxAdmin::Rpm.should_receive(:info).with('ruby')
described_class.info 'ruby'
end

it "dispatches to ubuntu lookup mechanism" do
LinuxAdmin::Distro.should_receive(:local).twice.and_return(LinuxAdmin::Distros.ubuntu)
LinuxAdmin::Distros::Distro.should_receive(:local).twice.and_return(LinuxAdmin::Distros.ubuntu)
LinuxAdmin::Deb.should_receive(:info).with('ruby')
described_class.info 'ruby'
end
Expand Down
2 changes: 1 addition & 1 deletion spec/physical_volume_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

describe LinuxAdmin::PhysicalVolume do
before(:each) do
LinuxAdmin::Distro.stub(:local => LinuxAdmin::Distros::Test.new)
LinuxAdmin::Distros::Distro.stub(:local => LinuxAdmin::Distros::Test.new)

@physical_volumes = <<eos
/dev/vda2:vg_foobar:24139776:-1:8:8:-1:32768:368:0:368:pxR32D-YkC2-PfHe-zOwb-eaGD-9Ar0-mAOl9u
Expand Down
6 changes: 3 additions & 3 deletions spec/rpm_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@
files and to do system management tasks (as in Perl). It is simple,
straight-forward, and extensible.
EOS
described_class.should_receive(:run!).
with(described_class::RPM_CMD, :params => {"-qi" => "ruby"}).
and_return(double(:output => data))
arguments = [described_class.rpm_cmd, :params => {"-qi" => "ruby"}]
result = AwesomeSpawn::CommandResult.new("", data, "", 0)
described_class.should_receive(:run!).with(*arguments).and_return(result)
metadata = described_class.info("ruby")
metadata['name'].should == 'ruby'
metadata['version'].should == '2.0.0.247'
Expand Down
4 changes: 2 additions & 2 deletions spec/service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
describe LinuxAdmin::Service do
before(:each) do
# stub distro.local to return test distro for command lookup
LinuxAdmin::Distro.stub(:local).
and_return(LinuxAdmin::Distros::Test.new)
LinuxAdmin::Distros::Distro.stub(:local)
.and_return(LinuxAdmin::Distros::Test.new)

@service = LinuxAdmin::Service.new 'foo'
end
Expand Down
3 changes: 3 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@

config.after do
clear_caches

# reset the distro, tested in various placed & used extensively
LinuxAdmin::Distros::Distro.instance_variable_set(:@local, nil)
end
end

Expand Down
2 changes: 1 addition & 1 deletion spec/volume_group_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

describe LinuxAdmin::VolumeGroup do
before(:each) do
LinuxAdmin::Distro.stub(:local => LinuxAdmin::Distros::Test.new)
LinuxAdmin::Distros::Distro.stub(:local => LinuxAdmin::Distros::Test.new)

@groups = <<eos
vg_foobar:r/w:772:-1:0:2:2:-1:0:1:1:12058624:32768:368:368:0:tILZUF-IspH-H90I-pT5j-vVFl-b76L-zWx3CW
Expand Down