rpmrepository 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/package.rb +199 -0
- data/lib/repofactory.rb +69 -0
- data/lib/repository.rb +63 -0
- data/lib/repository_api.rb +152 -0
- data/lib/repository_caching.rb +81 -0
- data/lib/repository_metadata.rb +72 -0
- data/lib/rpm.rb +16 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f0cdef908e95af3293de5d542c3eb9aa8c71a5c4
|
4
|
+
data.tar.gz: 6621f6ab0eebd01755f89fec3a352dca7e9832f3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7c3ea38b17f3952ae1ec2f5c5195e3c11ddaa7d41fb66c1bc727ecda338e8c82e721f424fa61bae4fefba2c4afb54c5e2e1ed585531419db070a6656b201f31b
|
7
|
+
data.tar.gz: 45e71797540b581ae48bfd34e7a3ae064e054af458bd73ac44b5e54b4aa0b5f2995657a7a3b844cf67d0dcd4fc770fbfe681ea8f2bc025b1a23f897bad273b67
|
data/lib/package.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
#One package with possible multiple locations
|
2
|
+
|
3
|
+
module RPM
|
4
|
+
|
5
|
+
class Package < Monitor
|
6
|
+
|
7
|
+
attr_reader :uris
|
8
|
+
attr_reader :name
|
9
|
+
attr_reader :version
|
10
|
+
attr_reader :release
|
11
|
+
attr_reader :architecture
|
12
|
+
attr_reader :size
|
13
|
+
attr_reader :file_size
|
14
|
+
attr_reader :digests
|
15
|
+
|
16
|
+
def initialize raw_uri
|
17
|
+
super()
|
18
|
+
@uris = []
|
19
|
+
synchronize! {
|
20
|
+
raise ArgumentError, "Only String or URI, not #{raw_uri.class}" unless raw_uri.kind_of? String or raw_uri.kind_of? URI::Generic
|
21
|
+
if raw_uri.kind_of? URI::Generic
|
22
|
+
uri = raw_uri.dup
|
23
|
+
else
|
24
|
+
uri = URI::parse raw_uri
|
25
|
+
end
|
26
|
+
if (uri.scheme == 'file' or uri.scheme == nil) and File.exist? uri.path
|
27
|
+
'file'
|
28
|
+
elsif uri.class == URI::HTTP and Net::HTTP.new(uri.host,uri.port).get(uri).is_a? Net::HTTPSuccess
|
29
|
+
'remote'
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Unreachable URI provided: #{uri.to_s}"
|
32
|
+
end
|
33
|
+
@uris.push uri
|
34
|
+
get_attributes
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def duplicate_to dst_dir, file_name = get_default_name
|
39
|
+
if dst_dir[/^\..*/]
|
40
|
+
dst_dir = File::expand_path dst_dir
|
41
|
+
end
|
42
|
+
raise ArgumentError, "Path must be absolute (#{dst_dir})" unless dst_dir[/^\//]
|
43
|
+
target_uri = URI::parse "file:#{dst_dir}/#{file_name}"
|
44
|
+
synchronize! {
|
45
|
+
return true if get_local_uris.include? target_uri
|
46
|
+
if uri = get_local_uris.first and not uri.nil?
|
47
|
+
raise Errno::EEXIST, "File #{File::basename target_uri.path} already exist!" if File.exist? target_uri.path
|
48
|
+
begin
|
49
|
+
FileUtils::link uri.path, target_uri.path
|
50
|
+
rescue Errno::EXDEV => e
|
51
|
+
FileUtils::cp uri.path, target_uri.path
|
52
|
+
end
|
53
|
+
else
|
54
|
+
get_remote_uris.each { |remote_uri|
|
55
|
+
begin
|
56
|
+
raise Errno::EEXIST, "File #{File::basename target_uri.path} already exist!" if File.exist? target_uri.path
|
57
|
+
response = Net::HTTP.new(remote_uri.host,remote_uri.port).get(remote_uri)
|
58
|
+
if response.is_a? Net::HTTPSuccess
|
59
|
+
File.open(target_uri.path, "w+") { |f|
|
60
|
+
f.write response.body
|
61
|
+
}
|
62
|
+
else
|
63
|
+
next
|
64
|
+
end
|
65
|
+
#check what we write?
|
66
|
+
break
|
67
|
+
rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, URI::InvalidURIError => e
|
68
|
+
next
|
69
|
+
end
|
70
|
+
}
|
71
|
+
end
|
72
|
+
@uris.push target_uri if File.exist? target_uri.path
|
73
|
+
return File.exist? target_uri.path
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
#Removes all URL's undo provided dir
|
78
|
+
def deduplicate_undo dir
|
79
|
+
synchronize! {
|
80
|
+
(get_local_uris_undo dir).each { |uri|
|
81
|
+
FileUtils::rm_f uri.path
|
82
|
+
@uris.delete uri
|
83
|
+
}
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
#Try to expand current package by other source uris
|
88
|
+
def join! other_package
|
89
|
+
synchronize! {
|
90
|
+
if same_as? other_package
|
91
|
+
@uris += other_package.uris
|
92
|
+
@uris.uniq!
|
93
|
+
return true
|
94
|
+
else
|
95
|
+
return false
|
96
|
+
end
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
#Remove each reachable uri
|
101
|
+
def destroy!
|
102
|
+
synchronize! {
|
103
|
+
get_local_uris.each { |victim|
|
104
|
+
FileUtils::rm_f victim.path
|
105
|
+
@uris.delete victim
|
106
|
+
}
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
#Return true if packages has same attributes
|
111
|
+
def same_as? other
|
112
|
+
other.is_a? RPM::Package and
|
113
|
+
@name == other.name and
|
114
|
+
@version == other.version and
|
115
|
+
@release == other.release and
|
116
|
+
@architecture == other.architecture and
|
117
|
+
@digests[:sha256] == other.digests[:sha256]
|
118
|
+
end
|
119
|
+
|
120
|
+
#Return expected RPM Package file name
|
121
|
+
def get_default_name
|
122
|
+
"#{@name}-#{@version}-#{@release}.#{@architecture}.rpm"
|
123
|
+
end
|
124
|
+
|
125
|
+
#Return local URI's undo spicified directory
|
126
|
+
def get_local_uris_undo dir
|
127
|
+
if dir[/^\..*/]
|
128
|
+
dir = File::expand_path dir
|
129
|
+
end
|
130
|
+
unless dir[/.*\/$/]
|
131
|
+
dir = dir + '/'
|
132
|
+
end
|
133
|
+
synchronize! {
|
134
|
+
get_local_uris.select { |uri| uri.path[/^#{dir}/] }
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
#Return every local uris
|
139
|
+
def get_local_uris
|
140
|
+
synchronize! { @uris.select { |uri| uri.scheme == nil or uri.scheme == "file" } }
|
141
|
+
end
|
142
|
+
|
143
|
+
#Return every remote uris
|
144
|
+
def get_remote_uris
|
145
|
+
synchronize! { @uris.select { |uri| uri.scheme != nil and uri.scheme != "file" } }
|
146
|
+
end
|
147
|
+
|
148
|
+
#Try to repair package with broken local URIs
|
149
|
+
def repair!
|
150
|
+
synchronize {
|
151
|
+
@uris -= @uris.collect { |uri| (uri.scheme == 'file' or uri.scheme == nil) and not File.exist? uri.path }
|
152
|
+
if @uris.empty?
|
153
|
+
raise RuntimeError, 'Lack of URIs for curent package!'
|
154
|
+
else
|
155
|
+
return true
|
156
|
+
end
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
#Get RPM package attributes on package creation
|
162
|
+
def get_attributes
|
163
|
+
tmp_file_dir = '/tmp/' + SecureRandom.uuid
|
164
|
+
tmp_file_name = tmp_file_dir + '/package.rpm'
|
165
|
+
FileUtils.mkdir tmp_file_dir
|
166
|
+
raise RuntimeError, "Can't get package to determine attributes" unless duplicate_to tmp_file_dir, 'package.rpm'
|
167
|
+
@name, @version, @release, @architecture, @size = `rpm -q --queryformat '%{NAME} %{VERSION} %{RELEASE} %{ARCH} %{SIZE}' -p #{tmp_file_name} 2> /dev/null`.split ' '
|
168
|
+
if @name.nil? or @version.nil? or @release.nil? or @architecture.nil?
|
169
|
+
raise RuntimeError, "Can't parse name from #{tmp_file_name} by rpm -q command"
|
170
|
+
end
|
171
|
+
raise RuntimeError, "Unexpected file #{tmp_file_name} disappearing!" unless File.file? tmp_file_name
|
172
|
+
@file_size = File.size tmp_file_name
|
173
|
+
@digests = { :sha1 => Digest::SHA1.hexdigest(File.read tmp_file_name), :sha256 => Digest::SHA256.hexdigest(File.read tmp_file_name) }
|
174
|
+
ensure
|
175
|
+
deduplicate_undo tmp_file_dir
|
176
|
+
FileUtils.rm_rf tmp_file_dir
|
177
|
+
end
|
178
|
+
|
179
|
+
#Check that local URIs point at actually exist file
|
180
|
+
def check_uris
|
181
|
+
@uris.each { |uri|
|
182
|
+
if (uri.scheme == 'file' or uri.scheme == nil) and not File.exist? uri.path
|
183
|
+
#MESSAGE MATTERS!
|
184
|
+
raise RuntimeError, "File #{uri.path} not exist, but mentioned in package"
|
185
|
+
end
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
#Synchronize with useful check - enshure that every synced action preceeded with local uris check
|
190
|
+
def synchronize!
|
191
|
+
synchronize {
|
192
|
+
check_uris
|
193
|
+
yield if block_given?
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
data/lib/repofactory.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#Attempt to sync concurrent managing of multiple repositories
|
2
|
+
|
3
|
+
module RPM
|
4
|
+
|
5
|
+
class RepoFactory
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@locker = Mutex.new
|
9
|
+
@locker.synchronize {
|
10
|
+
#Logging - now not needed
|
11
|
+
#@logger = Logging.logger['repo ' + @name]
|
12
|
+
#@logger.debug 'initializing'
|
13
|
+
@repositories = []
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
#Synced repository adding
|
18
|
+
def add_repository repository
|
19
|
+
@locker.synchronize {
|
20
|
+
if repository.kind_of? RPM::Repository
|
21
|
+
if (get_repository_by repository.name).is_a? RPM::Repository
|
22
|
+
raise ArgumentError, "Repository with such name already exist!"
|
23
|
+
end
|
24
|
+
@repositories.push repository
|
25
|
+
return true
|
26
|
+
else
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
#Synced repository removal and destroying
|
33
|
+
def destroy_repository_by! name
|
34
|
+
repo = get_repository_by name
|
35
|
+
return false if repo.nil?
|
36
|
+
repo.destroy!
|
37
|
+
@locker.synchronize {
|
38
|
+
@repositories.delete repo
|
39
|
+
}
|
40
|
+
return true
|
41
|
+
end
|
42
|
+
|
43
|
+
#the way to non-block reading
|
44
|
+
def get_repositories
|
45
|
+
@repositories.clone
|
46
|
+
end
|
47
|
+
|
48
|
+
#Return repository by name
|
49
|
+
def get_repository_by name
|
50
|
+
unless name.kind_of? String
|
51
|
+
return nil
|
52
|
+
end
|
53
|
+
possible_repos = get_repositories.select { |repo| repo.name == name }
|
54
|
+
case possible_repos.count
|
55
|
+
when 1
|
56
|
+
return possible_repos.first
|
57
|
+
else
|
58
|
+
return nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#wrap over Array's each
|
63
|
+
def each
|
64
|
+
get_repositories.each { |repository| yield repository if block_given? }
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
data/lib/repository.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#Local RPM repository with packages
|
2
|
+
|
3
|
+
module RPM
|
4
|
+
class Repository < Monitor
|
5
|
+
|
6
|
+
require_relative 'repository_api'
|
7
|
+
require_relative 'repository_caching'
|
8
|
+
require_relative 'repository_metadata'
|
9
|
+
|
10
|
+
attr_reader :base_dir
|
11
|
+
attr_reader :status
|
12
|
+
attr_reader :tmp_dir
|
13
|
+
attr_accessor :name
|
14
|
+
|
15
|
+
@@statuses = {
|
16
|
+
:initializing => "Repository initialization in progress",
|
17
|
+
:rebuilding => "Rebuild repository metadata",
|
18
|
+
:destroying => "Repository destruction in progress",
|
19
|
+
:destroyed => "Repository removed and not available",
|
20
|
+
:ok => "Repository in normal state",
|
21
|
+
:cleaning => "Cleaning up temporary data"
|
22
|
+
}
|
23
|
+
|
24
|
+
#Init repository in specified directory
|
25
|
+
def initialize base_dir, name = SecureRandom.uuid, rebuild_args = ""
|
26
|
+
#Call Monitor's contructor to initialize it
|
27
|
+
super()
|
28
|
+
synchronize {
|
29
|
+
@status = :initializing
|
30
|
+
#Human-readable repo name (default here, but available from the outside)
|
31
|
+
@name = name
|
32
|
+
#Logging
|
33
|
+
@logger = Logging.logger['repo ' + @name]
|
34
|
+
@logger.debug 'initializing'
|
35
|
+
#Local basic directory
|
36
|
+
raise ArgumentError, "Base directory must be non-relative" if base_dir.nil? or not base_dir[/^\/.+/]
|
37
|
+
FileUtils::mkdir_p base_dir, { :mode => 0700 } unless Dir.exist? base_dir
|
38
|
+
@base_dir = Dir.new base_dir
|
39
|
+
File.chmod 0700, @base_dir.path
|
40
|
+
#Dir for some tmp files
|
41
|
+
Dir.mkdir @base_dir.path + "/tmp" unless Dir.exist? @base_dir.path + "/tmp"
|
42
|
+
@tmp_dir = Dir.new @base_dir.path + "/tmp"
|
43
|
+
#Init repository
|
44
|
+
FileUtils::mkdir "#{@base_dir.path}/Packages", { :mode => 0700 } unless @base_dir.entries.include? "Packages"
|
45
|
+
#Parts-relates initialization
|
46
|
+
initialization_metadata rebuild_args
|
47
|
+
initialization_cache
|
48
|
+
@logger.info "initialized"
|
49
|
+
#Making MD
|
50
|
+
rebuild
|
51
|
+
#Cache warming
|
52
|
+
validate_packages_cache
|
53
|
+
@status = :ok
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
#Include substructures
|
58
|
+
include RPM::Repository::Metadata
|
59
|
+
include RPM::Repository::Caching
|
60
|
+
include RPM::Repository::API
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
#Public user api of RPM::Repository
|
2
|
+
#Should'n changed frequently
|
3
|
+
|
4
|
+
module RPM::Repository::API
|
5
|
+
|
6
|
+
#Add single package to repository
|
7
|
+
def add_package! package
|
8
|
+
add_packages! [package]
|
9
|
+
end
|
10
|
+
|
11
|
+
#Add packages to repository
|
12
|
+
def add_packages! packages
|
13
|
+
@logger.info "adding packages"
|
14
|
+
duplicated_packages = []
|
15
|
+
rebuild_with {
|
16
|
+
packages.each { |package|
|
17
|
+
raise ArgumentError, "Package expected, but #{package.class}" unless package.is_a? RPM::Package
|
18
|
+
raise RuntimeError, "Package already exist!" if contains? package
|
19
|
+
package.duplicate_to "#{@base_dir.path}/Packages"
|
20
|
+
#hack that add package to cache. Escape package brain splitting (two packages with the same file)
|
21
|
+
if package.digests[get_checksum_type.to_sym]
|
22
|
+
@packages_cache[package.digests[get_checksum_type.to_sym]] = package
|
23
|
+
end
|
24
|
+
duplicated_packages.push package
|
25
|
+
}
|
26
|
+
}
|
27
|
+
rescue Exception => e
|
28
|
+
rebuild_with {
|
29
|
+
duplicated_packages.each { |package|
|
30
|
+
package.deduplicate_undo "#{@base_dir.path}/Packages"
|
31
|
+
}
|
32
|
+
}
|
33
|
+
raise e
|
34
|
+
end
|
35
|
+
|
36
|
+
#Remove package
|
37
|
+
def remove_package! package
|
38
|
+
@logger.info "removing package"
|
39
|
+
rebuild_with {
|
40
|
+
raise ArgumentError, "No such package in repository: #{package.get_default_name}" unless contains? package
|
41
|
+
get_own(package).deduplicate_undo @base_dir.path
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
#Remove packages
|
46
|
+
#Return two arrays: {:removed => [...], :skipped => [...]}
|
47
|
+
def remove_packages! packages
|
48
|
+
@logger.info "removing packages"
|
49
|
+
rezult = {:removed => [], :skipped => []}
|
50
|
+
rebuild_with {
|
51
|
+
packages.each { |package|
|
52
|
+
if contains? package
|
53
|
+
get_own(package).deduplicate_undo @base_dir.path
|
54
|
+
rezult[:removed].push package
|
55
|
+
else
|
56
|
+
rezult[:skipped].push package
|
57
|
+
@logger.warn "Package #{package} skipped: no such package in repository"
|
58
|
+
end
|
59
|
+
}
|
60
|
+
}
|
61
|
+
return rezult
|
62
|
+
end
|
63
|
+
|
64
|
+
#Remove packages by regex
|
65
|
+
#Return list with removed URL's
|
66
|
+
def parse_out_packages! name_expression
|
67
|
+
return remove_packages! get_packages_list_by(name_expression)
|
68
|
+
end
|
69
|
+
|
70
|
+
#Return packages array that match parameter
|
71
|
+
def get_packages_list_by name_expression
|
72
|
+
get_packages_list.select{ |package| package.get_default_name[name_expression] }
|
73
|
+
end
|
74
|
+
|
75
|
+
#Return true if repository contains package with same signature
|
76
|
+
def contains? alien_package
|
77
|
+
not get_packages_list.select{ |package| package.same_as? alien_package }.empty?
|
78
|
+
end
|
79
|
+
alias include? contains?
|
80
|
+
|
81
|
+
#Try to expand package URIs with own
|
82
|
+
#Return true if package expanded, false otherwise
|
83
|
+
def assimilate! alien_package
|
84
|
+
if contains? alien_package
|
85
|
+
alien_package.join! get_own(alien_package)
|
86
|
+
synchronize { @packages_cache[alien_package.digests[get_checksum_type.to_sym]] = alien_package }
|
87
|
+
return true
|
88
|
+
else
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
#Remove repository files
|
94
|
+
def destroy!
|
95
|
+
@logger.info "destroying repo"
|
96
|
+
return @status if @status == :destroyed
|
97
|
+
synchronize {
|
98
|
+
@status = :destroying
|
99
|
+
clean
|
100
|
+
FileUtils::rm_rf @base_dir.path
|
101
|
+
@status = :destroyed
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
#Cleanup temporary files
|
106
|
+
def clean
|
107
|
+
@logger.info "cleaning repo"
|
108
|
+
synchronize {
|
109
|
+
@status = :cleaning
|
110
|
+
FileUtils::rm_rf @tmp_dir.path
|
111
|
+
FileUtils::mkdir @tmp_dir.path
|
112
|
+
@status = :ok
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
#Return template for YUM configuration
|
117
|
+
def get_local_conf
|
118
|
+
"
|
119
|
+
[#{@name}]
|
120
|
+
name=#{@name}
|
121
|
+
baseurl=file:///#{@base_dir.path}
|
122
|
+
gpgcheck=0
|
123
|
+
enabled=0
|
124
|
+
"
|
125
|
+
end
|
126
|
+
|
127
|
+
#Return array of packages
|
128
|
+
def get_packages_list
|
129
|
+
synchronize { validate_packages_cache && get_packages_from_cache }
|
130
|
+
end
|
131
|
+
|
132
|
+
#Public and safe realization of rebuilding - just rebuild repository
|
133
|
+
def rebuild
|
134
|
+
rebuild_with
|
135
|
+
end
|
136
|
+
private
|
137
|
+
|
138
|
+
#Return Package from current repository
|
139
|
+
#Method not public because it brings packages split-brain (two same package into runtime)
|
140
|
+
def get_own package
|
141
|
+
same_packages = get_packages_list.select { |repo_package| repo_package.same_as? package }
|
142
|
+
case same_packages.count
|
143
|
+
when 1
|
144
|
+
return same_packages.first
|
145
|
+
when 0
|
146
|
+
return nil
|
147
|
+
else
|
148
|
+
raise RuntimeError, "More than one #{package.get_default_name} package in repository"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#RPM Repository Caching module
|
2
|
+
#Mix for RPM::Repository to realize MD in-memory caching
|
3
|
+
|
4
|
+
module RPM::Repository::Caching
|
5
|
+
private
|
6
|
+
|
7
|
+
#Return values from cached packages list - UNSYNC!!!
|
8
|
+
def get_packages_from_cache
|
9
|
+
@packages_cache.values
|
10
|
+
end
|
11
|
+
|
12
|
+
#Check that current package list cache valid. if not - validate
|
13
|
+
def validate_packages_cache
|
14
|
+
@logger.debug "validating packages cache"
|
15
|
+
synchronize {
|
16
|
+
if repomd_cache_hit?
|
17
|
+
@logger.debug "MD cache hit"
|
18
|
+
return true
|
19
|
+
else
|
20
|
+
refresh_md_files_cache
|
21
|
+
refresh_packages_cache
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
#All below used just once. In case of multithreading usage - sync it by synchronize { body }
|
28
|
+
|
29
|
+
#true means 100% hit in cached repository state - UNSYNC!!! - used once
|
30
|
+
def repomd_cache_hit?
|
31
|
+
if @repomd_cache.is_a? REXML::Document
|
32
|
+
return read_repomd_doc.elements["/repomd/revision"].text == @repomd_cache.elements["/repomd/revision"].text
|
33
|
+
else
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
#refreshing each md file cach
|
39
|
+
def refresh_md_files_cache
|
40
|
+
@repomd_cache = read_repomd_doc
|
41
|
+
@md_content_cache.keys.each { |type|
|
42
|
+
@md_content_cache[type] = read_md_doc type.to_s
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
#Actually check packages cache: create new one and fill it with old cache and newly created one
|
47
|
+
def refresh_packages_cache
|
48
|
+
#save current cache state
|
49
|
+
prev_cache = @packages_cache
|
50
|
+
#clean current cache - used by other functions to refresh cache
|
51
|
+
@packages_cache = {}
|
52
|
+
@md_content_cache[:primary].each_element('/metadata/package') { |package_md|
|
53
|
+
chksum = package_md.elements['checksum'].text
|
54
|
+
if prev_cache[chksum]
|
55
|
+
#get package from previous cache
|
56
|
+
@packages_cache[chksum] = prev_cache[chksum]
|
57
|
+
else
|
58
|
+
#construct new one
|
59
|
+
url = ''
|
60
|
+
if package_md.elements['location'].attributes["xml:base"]
|
61
|
+
url = URI::parse (package_md.elements['location'].attributes["xml:base"]+'/'+package_md.elements['location'].attributes["href"])
|
62
|
+
else
|
63
|
+
url = URI::parse ('file://' + @base_dir.path+'/'+package_md.elements['location'].attributes["href"])
|
64
|
+
end
|
65
|
+
@packages_cache[chksum] = RPM::Package.new url
|
66
|
+
end
|
67
|
+
}
|
68
|
+
@logger.debug 'package cache refreshed'
|
69
|
+
end
|
70
|
+
|
71
|
+
#Cache-related part of initialization
|
72
|
+
def initialization_cache
|
73
|
+
#cached and parsed main XML MD file - repomd.xml
|
74
|
+
@repomd_cache = nil
|
75
|
+
#cached and parsed other XML documents
|
76
|
+
@md_content_cache = { :primary => nil }
|
77
|
+
#Repository Packages cache. Format: checksum => RPM::Package
|
78
|
+
@packages_cache = {}
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#RPM Repository Metadata module
|
2
|
+
#Mix for RPM::Repository to work witn MD
|
3
|
+
|
4
|
+
module RPM::Repository::Metadata
|
5
|
+
private
|
6
|
+
|
7
|
+
#Rebuild repository with current configuration with additional args and block - synced
|
8
|
+
def rebuild_with args = ""
|
9
|
+
synchronize {
|
10
|
+
yield if block_given?
|
11
|
+
@status = :rebuilding
|
12
|
+
#TODO: add group file to rebuilding
|
13
|
+
unless system "createrepo -v --profile --update #{@base_dir.path} -s #{get_checksum_type} #{@extended_rebuild_args} #{args} &> '#{@tmp_dir.path}/rebuild-#{Time.now.to_s}'"
|
14
|
+
raise RuntimeError, "Can't rebuild repository #{@name}"
|
15
|
+
end
|
16
|
+
@status = :ok
|
17
|
+
}
|
18
|
+
@logger.info "rebuilded"
|
19
|
+
return true
|
20
|
+
end
|
21
|
+
|
22
|
+
#read and parse repomd.xml - UNSYNC! - low level operation - no reason to make locks
|
23
|
+
def read_repomd_doc
|
24
|
+
REXML::Document.new File.open(@base_dir.path + "/repodata/repomd.xml")
|
25
|
+
end
|
26
|
+
|
27
|
+
#return md file document by type - UNSYNC!
|
28
|
+
#assume that @repomd_cache is valid
|
29
|
+
def read_md_doc type
|
30
|
+
@logger.debug "getting #{type} md file"
|
31
|
+
if @repomd_cache.elements["/repomd/data[@type=\"#{type}\"]/location"].attributes["href"]
|
32
|
+
path_to_md_file = @base_dir.path+'/'+@repomd_cache.elements["/repomd/data[@type=\"#{type}\"]/location"].attributes["href"]
|
33
|
+
#raw_md_file = File.read path_to_md_file
|
34
|
+
case path_to_md_file
|
35
|
+
when /\.gz$/
|
36
|
+
Zlib::GzipReader.open(path_to_md_file) { |gz|
|
37
|
+
return REXML::Document.new gz.read
|
38
|
+
}
|
39
|
+
when /\.xml$/
|
40
|
+
return REXML::Document.new File.read path_to_md_file
|
41
|
+
else
|
42
|
+
raise RuntimeError, "Can't determine type of #{path_to_md_file}"
|
43
|
+
end
|
44
|
+
else
|
45
|
+
raise ArgumentError, 'No #{type} md file record in repomd.xml'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#Get checksum type from repomd.xml to be used for correct rebuilding - UNSYNC!!!
|
50
|
+
def get_checksum_type
|
51
|
+
#@logger.debug "getting checksum type"
|
52
|
+
if File::exist? (@base_dir.path + "/repodata/repomd.xml")
|
53
|
+
repomd_doc = read_repomd_doc
|
54
|
+
if repomd_doc.elements["/repomd/data/checksum"].attributes["type"]
|
55
|
+
return repomd_doc.elements["/repomd/data/checksum"].attributes["type"]
|
56
|
+
else
|
57
|
+
#if no such field (improbable)
|
58
|
+
return "sha256"
|
59
|
+
end
|
60
|
+
else
|
61
|
+
#if this is fresh repository
|
62
|
+
return "sha256"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
#MD-related initialization part
|
67
|
+
def initialization_metadata rebuild_args
|
68
|
+
#Default additional arguments for createrepo
|
69
|
+
@extended_rebuild_args = rebuild_args
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
data/lib/rpm.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module RPM
|
4
|
+
|
5
|
+
require 'fileutils'
|
6
|
+
require 'securerandom'
|
7
|
+
require 'uri'
|
8
|
+
require 'net/http'
|
9
|
+
require 'rexml/document'
|
10
|
+
require 'logging'
|
11
|
+
|
12
|
+
require_relative 'package'
|
13
|
+
require_relative 'repository'
|
14
|
+
require_relative 'repofactory'
|
15
|
+
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rpmrepository
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- nothing
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-09-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: logging
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Gem to create/manage RPM repository(ies)/package(s)
|
28
|
+
email:
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/package.rb
|
34
|
+
- lib/repofactory.rb
|
35
|
+
- lib/repository.rb
|
36
|
+
- lib/repository_api.rb
|
37
|
+
- lib/repository_caching.rb
|
38
|
+
- lib/repository_metadata.rb
|
39
|
+
- lib/rpm.rb
|
40
|
+
homepage:
|
41
|
+
licenses:
|
42
|
+
- Beerware
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 2.6.11
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: Deal with RPM Repository/Package
|
64
|
+
test_files: []
|