workEnv 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.
@@ -0,0 +1,760 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'uri'
4
+ require 'tempfile'
5
+ require 'rbconfig'
6
+ require 'thread'
7
+
8
+ # Creates unique temporary directory in /tmp
9
+ def create_tmp_dir(tmp_dir_prefix=nil,tmp_root_dir=nil)
10
+ tmp_dir = nil
11
+
12
+ if(Dir.respond_to?('mktmpdir')) then
13
+ tmp_dir = Dir.mktmpdir(tmp_dir_prefix,tmp_root_dir)
14
+ else
15
+ tmp_root_dir = (tmp_root_dir.nil? ? Dir.tmpdir : tmp_root_dir)
16
+ tmp_dir_prefix = (tmp_dir_prefix.nil? ? "" : tmp_dir_prefix)
17
+ tmp_dir_name = File.join(tmp_root_dir,tmp_dir_prefix)
18
+ tmp_dir = `mktemp -d #{tmp_dir_name}XXXXXX`.chomp()
19
+ end
20
+ return tmp_dir
21
+ end
22
+
23
+ # Run a shell command and return its stdout
24
+ #
25
+ # Print command if verbose = false
26
+ #
27
+ # Returns the cimmand return status
28
+ def runCmd(cmd, silent = false, nRetry = 1)
29
+ ret = 0
30
+ 1.upto(nRetry){|i|
31
+ retrying = (i == 1 ? "" : "Retrying: ")
32
+ puts retrying + cmd if silent == false
33
+ if(RbConfig::CONFIG['host_os'] =~ /mingw/) then
34
+ script = Tempfile.new("script")
35
+ script.puts "set -x"
36
+ script.puts "set -e"
37
+ script.puts cmd
38
+ script.flush()
39
+ system("cat #{script.path}")
40
+ ret = `bash #{script.path}`.chomp()
41
+ script.close()
42
+ else
43
+ ret = `#{cmd}`.chomp()
44
+ end
45
+ return ret if $? == 0
46
+ }
47
+ raise("Command failed:\npwd=#{Dir.pwd()}\nCommand=#{cmd}\nReturn=#{ret}") if $? != 0
48
+ end
49
+
50
+ # Execute cmd and returns true in case of success, false otherwise
51
+ def runTest(cmd, silent = false, nRetry = 1)
52
+ ret = 1
53
+ 1.upto(nRetry){|i|
54
+ retrying = (i == 1 ? "" : "Retrying: ")
55
+ puts retrying + cmd if silent == false
56
+ if(RbConfig::CONFIG['host_os'] =~ /mingw/) then
57
+ script = Tempfile.new("script")
58
+ script.puts "set -x"
59
+ script.puts "set -e"
60
+ script.puts cmd
61
+ script.flush()
62
+ system("cat #{script.path}")
63
+ ret = `bash #{script.path}`.chomp()
64
+ script.close()
65
+ else
66
+ ret = `#{cmd}`.chomp()
67
+ end
68
+ return true if $? == 0
69
+ }
70
+ return false
71
+ end
72
+
73
+ module WorkEnvs
74
+ # Command to download a remote file through HTTP or HTTPS
75
+ WORK_WGET_CMD="wget -q --no-check-certificate"
76
+ # Command to check if a remote file exists (HTTP or HTTPS)
77
+ WORK_WGET_SPIDER_CMD="wget #{WorkEnvs::VERBOSE == true ? "" : "-q"} --no-check-certificate --spider"
78
+ # Command to copy a file
79
+ WORK_CP_CMD="cp"
80
+
81
+ # Exception thrown when a file was not found
82
+ class NoSuchFileException < StandardError
83
+ # Constructor
84
+ #
85
+ # - path = package path
86
+ # - server = locations searched
87
+ def initialize(path, server="")
88
+ super("\nERROR: File #{path} could not be found #{server}\n")
89
+ end
90
+ end
91
+
92
+ # Class used to manipulate packages
93
+ #
94
+ # This allows to query the DB for packages, query for dependencies (DB or files),
95
+ # download, extract packages and much more
96
+ class PackageDownloader
97
+ # Selected architectrue.
98
+ #
99
+ # This is a hash returned by WorkEnvs::getArch()
100
+ attr_reader :arch
101
+
102
+ # Table to use for DB queries (default = nil)
103
+ #
104
+ # Passed to the DBInterface #db_interface
105
+ attr_reader :table
106
+
107
+ # Global settings
108
+ attr_reader :settings
109
+
110
+ # DBInterface used for DB queries
111
+ attr_reader :db_interface
112
+
113
+ # Constructor
114
+ #
115
+ # - machine: label of the machine to run on. (default = nil)
116
+ # This is passed to WorkEnvs::getArch to fill the #arch attribute
117
+ # - table: Table to use for queries (default = nil)
118
+ # This is usually not set because the --table option modifies the settings
119
+ # and end up to be the default
120
+ # - silent: If false, activate verbosity (default = true)
121
+ def initialize(machine = nil, table=nil, silent = true)
122
+ @settings = WorkEnvs::settings[:db]
123
+ @arch = WorkEnvs::getArch(machine)
124
+ @table = table
125
+
126
+ @db_interface = DBInterface.new(machine, table, silent)
127
+ @be_silent = silent
128
+ @semaphore = Mutex.new
129
+ end
130
+
131
+ # Change the silent attribute for the PackageDownloader and its DBInterface
132
+ def be_silent=(val)
133
+ @be_silent = val
134
+ @db_interface.be_silent = val
135
+ end
136
+
137
+ # Modify a DBQuery to ignore specific version
138
+ #
139
+ # The goal of this is to handle package that moved from one project to
140
+ # another. This make sure we ignore the packages whose sha1
141
+ # matches SHA1 of the blacklisted project
142
+ def addBlacklist(db_query, blacklist)
143
+ if blacklist.length > 0 then
144
+ db_query.cond <<
145
+ { :field => "sha1", :not => true,
146
+ :sub_query => DBQuery.new({
147
+ :qtype => [ "sha1" ],
148
+ :cond => [ {:field => "arch",:value =>@arch[:label]},
149
+ {:field => "project",:value => blacklist},
150
+ ],
151
+ :limit => false,
152
+ :order => false
153
+ }).to_sql(@db_interface.table, true)[0]}
154
+ end
155
+ end
156
+
157
+ # Find the full package name of a package from its SHA1 and the #arch
158
+ #
159
+ # Returns a Package with its pack field filled or nil
160
+ #
161
+ # Throws EmptyQueryException if the package was not found and required = true
162
+ def queryPackage(package, sha1, required = false)
163
+ query = DBQuery.new({
164
+ :qtype => [ "name" ],
165
+ :cond => [ {:field => "sha1",:value =>sha1},
166
+ {:field => "arch",:value =>@arch[:label]},
167
+ {:field => "project",:value =>package.to_s}
168
+ ]
169
+ })
170
+
171
+ packageName = @db_interface.doQuery(query, required)
172
+ return nil if packageName == WORK_EMPTY_QUERY
173
+
174
+ if package.instance_of?(WorkEnvs::Package) then
175
+ package.pack = packageName[0][0]
176
+ else
177
+ package = Package.new(package, {}, packageName[0][0])
178
+ end
179
+ return package
180
+ end
181
+
182
+ # Find the version of a package from its SHA1 and the #arch
183
+ #
184
+ # Returns the version String or nil
185
+ #
186
+ # Throws EmptyQueryException if the package was not found and required = true
187
+ def queryPackageVersion(project, sha1, required = false)
188
+ query = DBQuery.new({
189
+ :qtype => [ "version" ],
190
+ :cond => [ {:field => "sha1",:value =>sha1},
191
+ {:field => "arch",:value =>@arch[:label]},
192
+ {:field => "project",:value =>project}
193
+ ]
194
+ })
195
+
196
+ packageVersion = @db_interface.doQuery(query, required)
197
+ if packageVersion then
198
+ return packageVersion[0][0]
199
+ else
200
+ return nil
201
+ end
202
+ end
203
+
204
+ # Find the version of a package and its SHA1 from a package
205
+ # full name and the #arch
206
+ #
207
+ # Returns dep_info (See #Dependencies) or nil
208
+ #
209
+ # Throws EmptyQueryException if the package was not found and required = true
210
+ def queryDepInfosFromName(env, name, required)
211
+ project = WorkEnvs::getMainPackage(env)
212
+ query = DBQuery.new({
213
+ :qtype => [ "version", "sha1" ],
214
+ :cond => [ {:field => "name",:value =>name},
215
+ {:field => "arch",:value =>@arch[:label]},
216
+ {:field => "project",:value =>project}
217
+ ]
218
+ })
219
+ addBlacklist(query, WorkEnvs::getBlackList(env))
220
+
221
+ packageVersion = @db_interface.doQuery(query, required)
222
+ return nil if packageVersion == WORK_EMPTY_QUERY
223
+ return {
224
+ :version => packageVersion[0][0],
225
+ :sha1 => packageVersion[0][1]
226
+ }
227
+ end
228
+
229
+ # Find the version of a package from its SHA1 and the #arch
230
+ #
231
+ # Returns dep_info (See #Dependencies) or nil
232
+ #
233
+ # Throws EmptyQueryException if the package was not found and required = true
234
+ def queryDepInfosFromSHA1(env, sha1, required)
235
+ project = WorkEnvs::getMainPackage(env)
236
+
237
+ short_sha1 = nil
238
+ if sha1 !~ /[0-9a-f]{40}/ then
239
+ short_sha1 = sha1
240
+ sha1 += "%"
241
+ end
242
+ query = DBQuery.new({
243
+ :qtype => [ "version", "sha1" ],
244
+ :cond => [ {:field => "sha1",:value =>sha1},
245
+ {:field => "arch",:value =>@arch[:label]},
246
+ {:field => "project",:value =>project}
247
+ ],
248
+ :limit => 1000
249
+ })
250
+ addBlacklist(query, WorkEnvs::getBlackList(env))
251
+
252
+ packageVersion = @db_interface.doQuery(query, required)
253
+ return nil if packageVersion == WORK_EMPTY_QUERY
254
+ if short_sha1 != nil then
255
+ if packageVersion.length > 1 then
256
+ raise("Multiple SHA1 matching the given prefix:\n" +
257
+ packageVersion.map(){|m| "\t#{m[1]}\t#{m[0]}\n"}.join(""))
258
+ else
259
+ puts "INFO: short sha1 #{short_sha1} expanded to (#{packageVersion[0][1]}, #{packageVersion[0][0]})"
260
+ end
261
+ end
262
+ return {
263
+ :version => packageVersion[0][0],
264
+ :sha1 => packageVersion[0][1]
265
+ }
266
+ end
267
+
268
+ # Find the SHA1 package from its version string and the #arch
269
+ #
270
+ # Returns dep_info (See #Dependencies) or nil
271
+ #
272
+ # Throws EmptyQueryException if the package was not found and required = true
273
+ def queryDepInfosFromVersion(env, version, required)
274
+ project = WorkEnvs::getMainPackage(env)
275
+ query = DBQuery.new({
276
+ :qtype => [ "sha1" ],
277
+ :cond => [ {:field => "version",:value =>version},
278
+ {:field => "arch",:value =>@arch[:label]},
279
+ {:field => "project",:value =>project}
280
+ ]
281
+ })
282
+ addBlacklist(query, WorkEnvs::getBlackList(env))
283
+
284
+ packageVersion = @db_interface.doQuery(query, required)
285
+ return nil if packageVersion == WORK_EMPTY_QUERY
286
+ return {
287
+ :version => version,
288
+ :sha1 => packageVersion[0][0]
289
+ }
290
+ end
291
+
292
+ # Return an array all package names using this SHA1 and this #arch
293
+ #
294
+ # Throws EmptyQueryException if no packages were not found and required = true
295
+ def queryMatchingSHA1(sha1, required = false)
296
+ query = DBQuery.new({
297
+ :qtype => [ "project" ],
298
+ :cond => [ {:field => "sha1",:value =>sha1},
299
+ {:field => "arch",:value =>@arch[:label]},
300
+ ],
301
+ :limit => 10000
302
+ })
303
+
304
+ packageList = @db_interface.doQuery(query, required)
305
+ return packageList.map(){|col| col[0]}
306
+ end
307
+
308
+ # Return an array all the branches that have packages for env
309
+ #
310
+ # Throws EmptyQueryException if no branches were not found
311
+ def queryBranches(env)
312
+ project = WorkEnvs::getMainPackage(env)
313
+ query = DBQuery.new({
314
+ :qtype => [ "branch" ],
315
+ :cond => [ {:field => "arch",:value =>@arch[:label]},
316
+ {:field => "project",:value =>project}
317
+ ],
318
+ :group_by => "branch",
319
+ :limit => 1000000
320
+ })
321
+ addBlacklist(query, WorkEnvs::getBlackList(env))
322
+ branches = @db_interface.doQuery(query, true, false)
323
+ return branches
324
+ end
325
+
326
+ # Query the limit last packages named name on teh select branch
327
+ #
328
+ # if branch = nil, all branches are looked at
329
+ #
330
+ # Returns an array of EnvPackage
331
+ def queryPackages(project, branch=nil, limit=100000, blacklist=[])
332
+ query = DBQuery.new({
333
+ :qtype => [ "name", "sha1", "branch", "info" ],
334
+ :cond => [ {:field => "arch",:value =>@arch[:label]},
335
+ {:field => "project",:value => project},
336
+ {:field => "branch",:value => branch}
337
+ ],
338
+ :limit => limit,
339
+ :order => "id",
340
+ :orderType => "desc"
341
+ })
342
+ addBlacklist(query, blacklist)
343
+
344
+ result = @db_interface.doQuery(query, true, false)
345
+ packages=[]
346
+ idx = 0
347
+ result.each() {|cols|
348
+ next if cols[1] == nil || cols[0] == nil
349
+ packages[idx] = EnvPackage.new(cols[0], cols[1], cols[2], cols[3])
350
+ idx += 1
351
+ }
352
+ return packages
353
+ end
354
+
355
+ # Get a package full path
356
+ #
357
+ # Looks at all the possible package repositories to find a package
358
+ # that matches package full name and arch
359
+ #
360
+ # Call by getPackagePath when path is not yet resolved
361
+ #
362
+ # Returns the package path
363
+ #
364
+ # Throws NoSuchFileException if the package could not be found and ignore_err = false
365
+ def resolvePackagePath(package, ignore_err = false)
366
+ packageName = package.pack.gsub(/^ */, "")
367
+ @settings[:package_repos].each(){|url|
368
+ type = url.split("://")[0]
369
+ case(type)
370
+ when "http", "https"
371
+ tables = @db_interface.table + @settings[:package_db_tables]
372
+ tables.uniq!
373
+ tables.each(){
374
+ |table|
375
+ path = "#{url}/#{table}/#{@arch[:distrib]}/#{@arch[:version]}/#{@arch[:arch]}/#{packageName}"
376
+ begin
377
+ runCmd("#{WORK_WGET_SPIDER_CMD} #{path}", @be_silent)
378
+ return path
379
+ rescue
380
+ return path if ignore_err == true
381
+ end
382
+ }
383
+ when "file"
384
+ base = url.split("://")[1]
385
+ path = "#{type}://#{base}/#{packageName}"
386
+ puts "Checking file: #{path}" if !@be_silent
387
+ if File.exist?("#{base}/#{packageName}")
388
+ return path
389
+ end
390
+ path2 = "#{type}://#{base}/#{@arch[:distrib]}/#{@arch[:version]}/#{@arch[:arch]}/#{packageName}"
391
+ puts "Checking file: #{path2}" if !@be_silent
392
+ if File.exist?("#{base}/#{@arch[:distrib]}/#{@arch[:version]}/#{@arch[:arch]}/#{packageName}")
393
+ return path2
394
+ end
395
+ return path if ignore_err == true
396
+ else
397
+ raise("Unsupported WORK_PACKAGE_REPO_PROTO: #{type}")
398
+ end
399
+ }
400
+ raise NoSuchFileException.new(package, "in any repositories") if ignore_err != true
401
+ end
402
+ private :resolvePackagePath
403
+
404
+ # Returns a Package path
405
+ #
406
+ # If the path is not set, call #resolvePackagePath to fill it
407
+ #
408
+ # Throws NoSuchFileException if the package could not be found and ignore_err = false
409
+ def getPackagePath(package, ignore_err = false)
410
+ return package.path if package.path != nil
411
+
412
+ package.path = resolvePackagePath(package, ignore_err)
413
+ return package.path
414
+ end
415
+
416
+ # Make sure that at least one package repository is accesible
417
+ #
418
+ # Throws an exception on failure
419
+ def checkRepo()
420
+ errors=[]
421
+ raise("No package source provided") if @settings[:package_repos] == nil || @settings[:package_repos].length == 0
422
+ @settings[:package_repos].each(){|url|
423
+ urlType = url.split('://')[0]
424
+ case(urlType)
425
+ when "http", "https"
426
+ path = "#{url}/#{@db_interface.table[0]}/"+
427
+ "#{@arch[:distrib]}/#{@arch[:version]}/#{@arch[:arch]}/"
428
+ begin
429
+ runCmd("#{WORK_WGET_SPIDER_CMD} #{path}", @be_silent)
430
+ return
431
+ rescue
432
+ errors << "Could not connect to server #{url}"
433
+ end
434
+ when "file"
435
+ base = url.split("://")[1]
436
+ if !File.exist?(base) then
437
+ errors << "Local package repository '#{base}' does not exists"
438
+ else
439
+ return
440
+ end
441
+ else
442
+ errors << "Unsupported WORK_PACKAGE_REPO_PROTO: #{url}"
443
+ end
444
+ }
445
+ raise("Could not find any accessible package repositories\n" + errors.join("\n"))
446
+ end
447
+
448
+ # Returns true if Packahe is a gzip package
449
+ def isGzipPackage?(package)
450
+ return runTest("file #{package} | grep \"gzip\" >/dev/null 2>&1 ", @be_silent)
451
+ end
452
+ private :isGzipPackage?
453
+
454
+ # Extracts all the dependencies from a RPM file and store them in deps
455
+ #
456
+ # Format
457
+ # dep[ depName ] :
458
+ # - [ :operator ] : <, <=, =, >= or >
459
+ # - [ :version ] : Version required
460
+ # - { :provided ] : true if it is provided for
461
+ #
462
+ # This is used to extract all dependencies (not internal to envs)
463
+ def getLocalPackageDependencies(packageName, deps = {})
464
+ raise("Feature only supported on RHEL/Centos/Fedora packages") if @arch[:base] != "RHEL"
465
+
466
+ return if(isGzipPackage?(packageName))
467
+
468
+ runCmd("rpm -qp --requires #{packageName}",
469
+ @be_silent).chomp().split("\n").each(){|line|
470
+
471
+ next if line !~ /([^ ]+)( +([<>=]+) +(.*))?$/
472
+
473
+ if deps[$1] != nil and $2 != nil then
474
+ package=$1
475
+ operator = $3
476
+ version = $4
477
+ if (deps[package][:version] !~ /#{version}(.el5)?/ &&
478
+ version !~ /#{deps[package][:version]}(.el5)?/) && deps[package][:version] == '=' &&
479
+ operator == '='
480
+ raise("Incompatible dependencies: Package '#{package}' is required in "+
481
+ "version #{deps[package][:version]} and #{version}")
482
+ end
483
+ elsif deps[$1] == nil
484
+ deps[$1] = {}
485
+ deps[$1][:operator] = $3
486
+ deps[$1][:version] = $4
487
+ end
488
+ }
489
+ runCmd("rpm -qp --provides #{packageName}",
490
+ @be_silent).chomp().split("\n").each(){|line|
491
+
492
+ next if line !~ /([^ ]+)( +([<>=]+) +(.*))?$/
493
+
494
+ if deps[$1] != nil and $2 != nil then
495
+ package=$1
496
+ version = $4
497
+ if (deps[package][:version] !~ /#{version}(.el5)?/ &&
498
+ version !~ /#{deps[package][:version]}(.el5)?/) && deps[package][:version] == '=' then
499
+ raise("Incompatible dependencies: Package '#{package}' is required in "+
500
+ "version #{deps[package][:version]} and #{version}")
501
+ end
502
+ deps[package][:provided] = true
503
+ elsif deps[$1] != nil
504
+ deps[$1][:provided] = true
505
+ else
506
+ deps[$1] = {}
507
+ deps[$1][:provided] = true
508
+ deps[$1][:operator] = $3
509
+ deps[$1][:version] = $4
510
+ end
511
+ }
512
+
513
+ return deps
514
+ end
515
+ private :getLocalPackageDependencies
516
+
517
+ # Download a Package
518
+ #
519
+ # Rename if to output if set
520
+ #
521
+ # The package is downloaded in the current directory
522
+ def download(package, output = nil)
523
+ path = getPackagePath(package)
524
+ output = File.basename(path) if output == nil
525
+ type = path.split("://")[0]
526
+ case type
527
+ when "http", "https"
528
+ runCmd("#{WORK_WGET_CMD} -O #{output} #{path}", @be_silent)
529
+ when "file"
530
+ base = path.split("://")[1]
531
+ runCmd("#{WORK_CP_CMD} #{base} #{output}", @be_silent)
532
+ else
533
+ raise("Unsupported protocol #{type}")
534
+ end
535
+ end
536
+
537
+ # Download a Package to the current directory
538
+ #
539
+ # Extract if if extract is true
540
+ #
541
+ # Erase afterwars if erase is set
542
+ #
543
+ def downloadAndExtract(package, extract, erase)
544
+ packageName = package.pack.gsub(/^ */, "")
545
+ runCmd("rm -f #{packageName}", @be_silent)
546
+
547
+ if @be_silent
548
+ puts "\t#{packageName}\n"
549
+ end
550
+ download(package)
551
+
552
+ return if(extract != true)
553
+
554
+
555
+ if(isGzipPackage?(packageName)) then
556
+ runCmd("tar --atime-preserve=system -zxf #{packageName} 2>&1 || "+
557
+ "tar -zxf #{packageName} 2>&1", @be_silent)
558
+ else
559
+ case(@arch[:base])
560
+ when "debian"
561
+ runCmd("dpkg --extract #{packageName} . 2>&1", @be_silent)
562
+ when "RHEL"
563
+ runCmd("rpm2cpio #{packageName} | cpio -id 2>&1", @be_silent, 5)
564
+ else
565
+ raise("Invalid package type")
566
+ end
567
+ end
568
+ if deps != nil
569
+ getLocalPackageDependencies(packageName, deps)
570
+ end
571
+ runCmd("rm -f #{packageName}", @be_silent) if erase == true
572
+ end
573
+
574
+ # Remove a the installed versions of Packages from the system (RPM/RHEL only)
575
+ #
576
+ # Throws an exception in uninstall failed
577
+ #
578
+ # Called by installPackages to remvoe former packages prior to enw install
579
+ def removePackage(package)
580
+ package = package.pack.gsub(/^ */, "")
581
+ raise ("Package '#{package}' is missing") if !File.exist?(package)
582
+
583
+ driver=runCmd("rpm -qp --qf='%{NAME}' #{package}", @be_silent)
584
+ installed = system("rpm -q #{driver}")
585
+ if(installed) then
586
+ begin
587
+ @semaphore.synchronize {
588
+ runCmd("sudo yum -y -q remove #{driver} || sudo yum -y -q remove #{driver}", @be_silent)
589
+ }
590
+ rescue => e
591
+ # Because we do it in //, we may have a race condition to uninstall this one
592
+ # So if it failed, just check if it's still there. If not, don't bother
593
+ installed = system("rpm -q #{driver}")
594
+ raise e if installed
595
+ end
596
+ end
597
+ end
598
+ private :removePackage
599
+
600
+ # Install packages on the system (yum, dpkg)
601
+ #
602
+ # Remove them prior to install using #removePackage
603
+ #
604
+ # Throws exception on unsupported arch, uninstall or install failure
605
+ def installPackages(packages)
606
+ pack_list=[]
607
+ packages.each(){|pack|
608
+
609
+ removePackage(pack)
610
+ package = pack.pack.gsub(/^ */, "")
611
+ raise ("Package '#{package}' is missing") if !File.exist?(package)
612
+
613
+ pack_list << package
614
+ }
615
+ case(@arch[:base])
616
+ when "debian"
617
+ raise("Do not know how to install DEB packages automatically")
618
+ when "RHEL"
619
+ puts "Installing"
620
+ runCmd("sudo rpm -i #{pack_list.join(" ")}", @be_silent)
621
+ else
622
+ raise("Invalid package type")
623
+ end
624
+ end
625
+
626
+ # Fetch the full names of two lists of Package matching sha1 and #arch
627
+ #
628
+ # This is used to generate a list of Package needed when updating an env
629
+ #
630
+ # Throws EmptyQueryException if a package in required could not be found
631
+ #
632
+ # Returns an array of Packages
633
+ def getPackagesName(required, extras, sha1)
634
+ packages = []
635
+ required.each() {|p|
636
+ package = queryPackage(p, sha1, true)
637
+
638
+ next if package == nil
639
+
640
+ list = package.pack.split("\n")
641
+ if list.length > 1 then
642
+ STDERR.puts "###################################################"
643
+ STDERR.puts "# ERROR: Query returned more than one package #"
644
+ STDERR.puts "# in a single DB entry..... #"
645
+ list.each(){|pack|
646
+ STDERR.puts "# => #{pack}".ljust(50) + "#"
647
+ }
648
+ STDERR.puts "###################################################"
649
+ raise()
650
+ end
651
+ packages << package
652
+ }
653
+ extras.each() {|p|
654
+ package = queryPackage(p, sha1, false)
655
+ next if package == nil
656
+
657
+ list = package.pack.split("\n")
658
+ if list.length > 1 then
659
+ STDERR.puts "###################################################"
660
+ STDERR.puts "# ERROR: Query returned more than one package #"
661
+ STDERR.puts "# in a single DB entry..... #"
662
+ list.each(){|pack|
663
+ STDERR.puts "# => #{pack}".ljust(50) + "#"
664
+ }
665
+ STDERR.puts "###################################################"
666
+ raise()
667
+ end
668
+ packages << package
669
+ }
670
+ return packages
671
+ end
672
+
673
+ # Extract dependencies (RPM, Deb) from a Package
674
+ #
675
+ # - Try to fetch the dependencies from the DB
676
+ # - On failure
677
+ # - Get the dependencies from the package
678
+ # - Publish them to the dependencies DB
679
+ #
680
+ # Returns a list of dependencies string
681
+ def listDependencies(pack)
682
+ remotePack = getPackagePath(pack)
683
+ extname = File.extname(remotePack)
684
+ return [] if(extname != ".deb" and extname != ".rpm")
685
+ res = nil
686
+
687
+ file = runCmd("mktemp", true)
688
+
689
+ tables = @db_interface.table + @settings[:package_db_tables]
690
+ tables.uniq!
691
+
692
+ # Try to do it on the SQL server first
693
+ query = DBDepsQuery.new({
694
+ :qtype => [ "dependencies" ],
695
+ :cond => [ {:field => "name",:value => pack.pack },
696
+ {:field => "arch",:value =>@arch[:label] }
697
+ ],
698
+ :limit => 1,
699
+ })
700
+ begin
701
+ res = @db_interface.doDepQuery(query)[0][0].to_s.split("\n")
702
+ return res
703
+ rescue => e
704
+ end
705
+
706
+ runCmd("rm -f #{file}", true)
707
+ case(@arch[:base])
708
+ when "RHEL"
709
+ res = runCmd("rpm -qp --requires #{remotePack} 2>/dev/null ", @be_silent).split("\n")
710
+ when "debian"
711
+ file = runCmd("mktemp", true)
712
+ download(pack, file)
713
+ res = runCmd("dpkg --info #{file} | /bin/grep -E '^ Depends:'",
714
+ @be_silent).gsub(/^ Depends: /, '').split(", ")
715
+ runCmd("rm -f #{file}", @be_silent)
716
+ else
717
+ raise("Invalid package type")
718
+ end
719
+
720
+ begin
721
+ query = DBDepsQuery.new({
722
+ :cond => [ {:field => "name",:value => pack.pack },
723
+ {:field => "arch",:value => @arch[:label] },
724
+ { :field => "dependencies", :value => res.join("\n") },
725
+ ],
726
+ :insert => true
727
+ })
728
+ @db_interface.doInsertDep(query)
729
+ rescue
730
+ end
731
+ return res
732
+ end
733
+
734
+ # Look for depenency to the package name in a list generated by #listDependencies
735
+ #
736
+ # Return the version of name required
737
+ def extractDependency(dependencies, name)
738
+ res = nil
739
+ case(@arch[:base])
740
+ when "RHEL"
741
+ dependencies.each(){|line|
742
+ next if line !~ /#{name}[[:space:]]*=/
743
+ res = line.gsub(/\.(fc|el)[0-9]*$/, '').gsub(/.*=[[:space:]]*(.*)$/, '\1')
744
+ puts "Dependency to: #{name} = #{res} " if @be_silent == false
745
+ return res
746
+ }
747
+ when "debian"
748
+ dependencies.each(){|line|
749
+ next if line !~ /^#{name}[[:space:]]*\(/
750
+ res = line.gsub(/.*=[[:space:]]*/, '').gsub(/\)/, '')
751
+ puts "Dependency to: #{name} = #{res} " if @be_silent == false
752
+ return res
753
+ }
754
+ else
755
+ raise("Invalid package type")
756
+ end
757
+ return res
758
+ end
759
+ end
760
+ end