Skip to content

Commit 1d8b067

Browse files
ShimShteinstejskalleos
authored andcommitted
Fixes #38930 - Make fact parsers use proper os
Make sure the fact parser searches by all unique constraint combinations before creating a new object.
1 parent 11df69c commit 1d8b067

File tree

11 files changed

+383
-63
lines changed

11 files changed

+383
-63
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
module OperatingSystemNaming
2+
extend ActiveSupport::Concern
3+
4+
module ClassMethods
5+
# Find all operatingsystems that are duplicates of the given operating system according to all unique constraints
6+
def find_by_attributes(name: nil, major: nil, minor: nil, description: nil)
7+
where_attributes = {
8+
name: name,
9+
major: major,
10+
minor: minor,
11+
}.compact
12+
scope = where(where_attributes)
13+
scope = scope.or(where(description: description)) if description.present?
14+
scope.or(where(title: generate_title(**where_attributes.merge(description: description))))
15+
end
16+
17+
def title_name
18+
"title".freeze
19+
end
20+
21+
def generate_title(description:, name:, major:, minor:)
22+
to_label(description: description, name: name, major: major, minor: minor).to_s[0..254]
23+
end
24+
25+
def to_label(description:, name:, major:, minor:)
26+
return description if description.present?
27+
fullname(name: name, major: major, minor: minor)
28+
end
29+
30+
def fullname(name:, major:, minor:)
31+
"#{name} #{release(major: major, minor: minor)}"
32+
end
33+
34+
def release(major:, minor:)
35+
"#{major}#{('.' + minor.to_s) if minor.present?}"
36+
end
37+
38+
def find_by_to_label(str)
39+
os = find_by_description(str.to_s)
40+
return os if os
41+
name, version = str.split(" ")
42+
cond = {:name => name}
43+
if version
44+
(major, minor) = os_major_minor_from_version_str(name, version)
45+
cond[:major] = major if major
46+
cond[:minor] = minor if minor
47+
end
48+
find_by(cond)
49+
end
50+
51+
def os_major_minor_from_version_str(os_name, version_str)
52+
if os_name == 'Ubuntu'
53+
x, y, minor = version_str.split('.', 3)
54+
major = "#{x}.#{y}"
55+
else
56+
major, minor = version_str.split('.')
57+
end
58+
[major, minor]
59+
end
60+
end
61+
end

app/models/operatingsystem.rb

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class Operatingsystem < ApplicationRecord
66
include Authorizable
77
include ValidateOsFamily
88
include PxeLoaderSupport
9+
include OperatingSystemNaming
910
extend FriendlyId
1011
friendly_id :title
1112

@@ -95,10 +96,6 @@ class Jail < Safemode::Jail
9596
allow :id, :name, :major, :minor, :family, :to_s, :==, :release, :release_name, :kernel, :initrd, :pxe_type, :boot_files_uri, :password_hash, :mediumpath, :bootfile
9697
end
9798

98-
def self.title_name
99-
"title".freeze
100-
end
101-
10299
def additional_media(medium_provider)
103100
medium_provider.additional_media.map(&:with_indifferent_access)
104101
end
@@ -141,8 +138,7 @@ def self.families_as_collection
141138

142139
# The OS is usually represented as the concatenation of the OS and the revision
143140
def to_label
144-
return description if description.present?
145-
fullname
141+
self.class.to_label(description: description, name: name, major: major, minor: minor)
146142
end
147143

148144
# to_label setter updates description and does not try to parse and update major, minor attributes
@@ -155,40 +151,17 @@ def to_param
155151
end
156152

157153
def release
158-
"#{major}#{('.' + minor.to_s) if minor.present?}"
154+
self.class.release(major: major, minor: minor)
159155
end
160156

161157
def fullname
162-
"#{name} #{release}"
158+
self.class.fullname(name: name, major: major, minor: minor)
163159
end
164160

165161
def to_s
166162
fullname
167163
end
168164

169-
def self.find_by_to_label(str)
170-
os = find_by_description(str.to_s)
171-
return os if os
172-
name, version = str.split(" ")
173-
cond = {:name => name}
174-
if version
175-
(major, minor) = os_major_minor_from_version_str(name, version)
176-
cond[:major] = major if major
177-
cond[:minor] = minor if minor
178-
end
179-
find_by(cond)
180-
end
181-
182-
def self.os_major_minor_from_version_str(os_name, version_str)
183-
if os_name == 'Ubuntu'
184-
x, y, minor = version_str.split('.', 3)
185-
major = "#{x}.#{y}"
186-
else
187-
major, minor = version_str.split('.')
188-
end
189-
[major, minor]
190-
end
191-
192165
# Implemented only in the OSs subclasses where it makes sense
193166
def available_loaders
194167
["None", "PXELinux BIOS"]
@@ -384,7 +357,7 @@ def set_family
384357
end
385358

386359
def set_title
387-
self.title = to_label.to_s[0..254]
360+
self.title = self.class.generate_title(description: description, name: name, major: major, minor: minor)
388361
end
389362

390363
def stringify_major_and_minor

app/services/foreman_ansible/operating_system_parser.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def operatingsystem
88
args[:release_name] = os_release_name if os_name == 'Debian' || os_name == 'Ubuntu'
99
# for Ansible, the CentOS Stream can be identified by missing minor version only
1010
args[:name] = "CentOS_Stream" if os_name == 'CentOS' && os_minor.blank?
11+
args[:description] = os_description
1112
return @local_os if local_os(args).present?
1213
return @new_os if new_os(args).present?
1314
logger.debug do
@@ -20,15 +21,19 @@ def operatingsystem
2021
end
2122

2223
def local_os(args)
23-
@local_os = Operatingsystem.where(args).first
24+
@local_os = Operatingsystem.find_by_attributes(**args.slice(:name, :major, :minor, :description)).first
2425
end
2526

2627
def new_os(args)
2728
return @new_os if @new_os.present?
28-
@new_os = Operatingsystem.new(args.merge(:description => os_description))
29+
@new_os = generate_new_os(args)
2930
@new_os if @new_os.valid? && @new_os.save
3031
end
3132

33+
def generate_new_os(args)
34+
Operatingsystem.new(args.merge(:description => os_description))
35+
end
36+
3237
def debian_os_major_sid
3338
release = if facts[:ansible_distribution_major_version] == 'n/a'
3439
facts[:ansible_distribution_release]

app/services/foreman_chef/fact_parser.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def operatingsystem
5151
end
5252

5353
args = { :name => os_name, :major => major.to_s, :minor => minor.to_s }
54-
klass.where(args).first || klass.new(args.merge(:release_name => release_name)).tap(&:save!)
54+
Operatingsystem.find_by_attributes(**args).first || klass.new(args.merge(:release_name => release_name)).tap(&:save!)
5555
end
5656

5757
# we don't want to parse puppet environment, foreman_chef already has its own environment

app/services/foreman_salt/fact_parser.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ class FactParser < ::FactParser
33
attr_reader :facts
44

55
def operatingsystem
6-
os = Operatingsystem.where(os_hash).first_or_initialize
6+
os = Operatingsystem.find_by_attributes(**os_hash).first || Operatingsystem.new(os_hash)
77
if os.new_record?
88
os.deduce_family
99
os.release_name = facts[:oscodename] || facts[:lsb_distrib_codename]

app/services/katello/rhsm_fact_parser.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ def operatingsystem
7373
os_attributes[:name] = "CentOS"
7474
end
7575

76-
os = ::Operatingsystem.find_by(os_attributes)
76+
os = ::Operatingsystem.find_by_attributes(**os_attributes.slice(:name, :major, :minor, :description)).first
7777
if os.blank?
78-
created = ::Operatingsystem.create_or_find_by(os_attributes)
78+
created = ::Operatingsystem.create(os_attributes)
7979
created.errors.any? ? ::Operatingsystem.find_by(os_attributes) : created
8080
else
8181
os

app/services/puppet_fact_parser.rb

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ class PuppetFactParser < FactParser
44
def operatingsystem
55
orel = os_release.dup
66

7+
args = { :name => os_name }
8+
79
if orel.present?
810
if os_name =~ /ubuntu/i
911
major = os_major_version.to_s
@@ -13,29 +15,24 @@ def operatingsystem
1315
major = major.to_s.gsub(/\D/, '')
1416
minor = minor.to_s.gsub(/[^\d\.]/, '')
1517
end
16-
args = {:name => os_name, :major => major, :minor => minor}
17-
os = Operatingsystem.find_or_initialize_by(args)
18-
if os_name[/debian|ubuntu/i] || os.family == 'Debian'
19-
if distro_codename
20-
os.release_name = distro_codename
21-
elsif os.release_name.blank?
22-
os.release_name = 'unknown'
23-
end
18+
args[:major] = major
19+
args[:minor] = minor
20+
if os_name[/debian|ubuntu/i]
21+
args[:release_name] = distro_codename if distro_codename
22+
args[:release_name] = 'unknown' if args[:release_name].blank?
2423
end
25-
else
26-
os = Operatingsystem.find_or_initialize_by(:name => os_name)
2724
end
2825

29-
if os.description.blank?
30-
if os_name == 'SLES'
31-
os.description = os_name + ' ' + orel.gsub('.', ' SP')
32-
elsif distro_description
33-
family = os.deduce_family || 'Operatingsystem'
34-
os = os.becomes(family.constantize)
35-
os.description = os.shorten_description(distro_description)
36-
end
26+
if os_name == 'SLES'
27+
args[:description] = "#{os_name} #{orel.gsub('.', ' SP')}"
28+
elsif distro_description
29+
family = Operatingsystem.deduce_family(os_name) || 'Operatingsystem'
30+
os_class = family.constantize
31+
args[:description] = os_class.new(args).shorten_description(distro_description)
3732
end
3833

34+
os = Operatingsystem.find_by_attributes(**args.slice(:name, :major, :minor, :description)).first || Operatingsystem.new(args)
35+
3936
if os.new_record?
4037
os.save!
4138
Operatingsystem.find_by_id(os.id) # complete reload to be an instance of the STI subclass

0 commit comments

Comments
 (0)