rpmrepository 0.1.0
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/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: []
|