pandler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/.gitignore +23 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +47 -0
  5. data/Rakefile +10 -0
  6. data/Vagrantfile +33 -0
  7. data/bin/pandle +8 -0
  8. data/etc/yum/pandler.py +52 -0
  9. data/lib/pandler.rb +8 -0
  10. data/lib/pandler/chroot.rb +229 -0
  11. data/lib/pandler/cli.rb +40 -0
  12. data/lib/pandler/version.rb +3 -0
  13. data/lib/pandler/yumfile.rb +26 -0
  14. data/lib/pandler/yumrepo.rb +240 -0
  15. data/pandler.gemspec +24 -0
  16. data/spec/pandler/chroot_spec.rb +93 -0
  17. data/spec/pandler/cli_spec.rb +20 -0
  18. data/spec/pandler/yumfile_spec.rb +20 -0
  19. data/spec/pandler/yumrepo_spec.rb +152 -0
  20. data/spec/pandler_spec.rb +8 -0
  21. data/spec/resources/yumrepo/pandler-test-0.0.1-1.x86_64.rpm +0 -0
  22. data/spec/resources/yumrepo/pandler-test-a-0.0.1-1.x86_64.rpm +0 -0
  23. data/spec/resources/yumrepo/pandler-test-a-0.0.2-1.x86_64.rpm +0 -0
  24. data/spec/resources/yumrepo/pandler-test-dep-a-0.0.1-1.x86_64.rpm +0 -0
  25. data/spec/resources/yumrepo/pandler-test-dep-b-0.0.1-1.x86_64.rpm +0 -0
  26. data/spec/resources/yumrepo/pandler-test-dep-required-0.0.1-1.x86_64.rpm +0 -0
  27. data/spec/resources/yumrepo/pandler-test-dep-required-required-0.0.1-1.x86_64.rpm +0 -0
  28. data/spec/resources/yumrepo/repodata/09267893997a9b34726567132f642093ebd2a4b5e65096a6562efc3a29106723-other.xml.gz +0 -0
  29. data/spec/resources/yumrepo/repodata/596e9069dad80db19e7ba4c36b3df1e52583231b95bfa61186408c77520a213a-primary.xml.gz +0 -0
  30. data/spec/resources/yumrepo/repodata/7dd30f881bb1b1cbd723a5fa9d996b96805e25015b56140c99a444bf248c4260-other.sqlite.bz2 +0 -0
  31. data/spec/resources/yumrepo/repodata/8e6f832a8605163914b5aaf46e42daeaad10f9785d202c6c99c9d903354b3194-filelists.xml.gz +0 -0
  32. data/spec/resources/yumrepo/repodata/a810cd5676bb578a196231dae1f37179241630f6745a3d33691279ff61bfe175-primary.sqlite.bz2 +0 -0
  33. data/spec/resources/yumrepo/repodata/ad696c5b02dadaef085b16712ce0464ae0d4c102b3ccf1ce0f14e371a119463a-filelists.sqlite.bz2 +0 -0
  34. data/spec/resources/yumrepo/repodata/repomd.xml +55 -0
  35. data/spec/spec_helper.rb +82 -0
  36. data/vagrant_file.sh +30 -0
  37. metadata +156 -0
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .*.swp
19
+ bin/*
20
+ !bin/pandle
21
+ .vagrant
22
+ vendor/bundle
23
+ spec/resources/repo
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pandler.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ryosuke IWANAGA
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,47 @@
1
+ # Pandler
2
+ Manage your packages with chroot.
3
+
4
+ Pandler(= Package + Bundler) helps managing rpm/yum packages. Using `Yumfile` and `Yumfile.lock`, Pandler automatically creates a locked chroot environment.
5
+
6
+ ## Installation
7
+
8
+ *Currently Pandler support only root user usage because of `mount`. You should install and run as root user.*
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'pandler'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install pandler
21
+
22
+ ## Usage
23
+
24
+ Write `Yumfile`.
25
+
26
+ repo "base", "http://vault.centos.org/6.2/os/x86_64/"
27
+
28
+ rpm "basesystem"
29
+ rpm ""
30
+
31
+ Run `pandle install`.
32
+
33
+ # pandle install
34
+ # pandle list
35
+
36
+ Then, you can execute any command in the chroot environment.
37
+
38
+ # pandle exec pwd
39
+ /
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => [:spec]
4
+ begin
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.rspec_opts = %w[-cfs -r ./spec/spec_helper.rb]
8
+ end
9
+ rescue LoadError => e
10
+ end
@@ -0,0 +1,33 @@
1
+ Vagrant.configure("2") do |vagrant|
2
+ define_list = [
3
+ {
4
+ :name => :centos,
5
+ :box => "Berkshelf-CentOS-6.3-x86_64-minimal",
6
+ :box_url => "http://dl.dropbox.com/u/31081437/Berkshelf-CentOS-6.3-x86_64-minimal.box",
7
+ :pkgm => :yum,
8
+ },
9
+ # {
10
+ # :name => :ubuntu,
11
+ # :box => "precise64",
12
+ # :box_url => "http://files.vagrantup.com/precise64.box",
13
+ # :pkgm => :apt,
14
+ # },
15
+ ]
16
+
17
+ vagrant.vm.provider :virtualbox do |v|
18
+ v.customize ["modifyvm", :id, "--memory", 256]
19
+ v.customize ["modifyvm", :id, "--cpus", 4]
20
+ end
21
+
22
+ define_list.each_with_index do |define, i|
23
+ vagrant.vm.define define[:name] do |config|
24
+ config.vm.box = define[:box]
25
+ config.vm.box_url = define[:box_url]
26
+
27
+ config.vm.hostname = define[:name].to_s
28
+ config.vm.network :private_network, ip: "192.168.50.#{i + 10}"
29
+
30
+ config.vm.provision :shell, :path => "vagrant_file.sh"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path("../../lib", __FILE__)
4
+
5
+ require "pandler"
6
+ require "pandler/cli"
7
+
8
+ Pandler::CLI.start(ARGV)
@@ -0,0 +1,52 @@
1
+ from yum.plugins import PluginYumExit, TYPE_INTERACTIVE
2
+ import sys
3
+ import pprint
4
+
5
+ requires_api_version = '2.1'
6
+ plugin_type = (TYPE_INTERACTIVE,)
7
+
8
+ def config_hook(conduit):
9
+ parser = conduit.getOptParser()
10
+ if hasattr(parser, 'plugin_option_group'):
11
+ parser = parser.plugin_option_group
12
+
13
+ def postresolve_hook(conduit):
14
+ persistdir = conduit._base._conf.persistdir
15
+ pkgs = [x[0] for x in conduit.getTsInfo().pkgdict.values()]
16
+
17
+ file = open(persistdir+"/pandler.log", "w")
18
+ for pkg in pkgs:
19
+ relatedto_list = [x[0] for x in set(pkg.relatedto)]
20
+
21
+ dic = {
22
+ "package" : __pkg_to_s(pkg),
23
+ "name" : pkg.name,
24
+ "version" : pkg.version,
25
+ "release" : pkg.release,
26
+ "arch" : pkg.arch,
27
+ }
28
+ if len(relatedto_list) == 0:
29
+ ltsv = u"\t".join(k + u":" + v for k, v in dic.iteritems())
30
+ # print(ltsv)
31
+ file.write(ltsv + "\n")
32
+ else:
33
+ for relatedto in relatedto_list:
34
+ dic["relatedto"] = __pkg_to_s(relatedto)
35
+ ltsv = u"\t".join(k + u":" + v for k, v in dic.iteritems())
36
+ # print(ltsv)
37
+ file.write(ltsv + "\n")
38
+
39
+ file.close()
40
+
41
+ def postdownload_hook(conduit):
42
+ opts, commands = conduit.getCmdLine()
43
+ # Don't die on errors, or we'll never see them.
44
+ if not conduit.getErrors():
45
+ sys.exit(0)
46
+
47
+ def __pkg_to_s(pkg):
48
+ name = pkg.name
49
+ version = pkg.version
50
+ release = pkg.release
51
+ arch = pkg.arch
52
+ return "%(name)s-%(version)s-%(release)s.%(arch)s" % locals()
@@ -0,0 +1,8 @@
1
+ require "pandler/version"
2
+ require "pandler/yumfile"
3
+ require "pandler/chroot"
4
+ require "pandler/yumrepo"
5
+
6
+ module Pandler
7
+ # Your code goes here...
8
+ end
@@ -0,0 +1,229 @@
1
+ require "etc"
2
+ require "open3"
3
+
4
+ class Pandler::Chroot
5
+ attr_reader :base_dir, :root_dir, :yumrepo, :mounts
6
+
7
+ def initialize(args = {})
8
+ @base_dir = args[:base_dir] || File.expand_path("pandler")
9
+ @root_dir = args[:root_dir] || File.join(base_dir, "root")
10
+ @yumrepo = args[:yumrepo] || "file://" + File.join(base_dir, "yumrepo")
11
+
12
+ @mounts = [
13
+ { :type => 'proc', :path => '/proc' },
14
+ { :type => 'sysfs', :path => '/sys' },
15
+ { :type => 'tmpfs', :path => '/dev/shm' },
16
+ { :type => 'devpts', :path => '/dev/pts',
17
+ :options => "gid=#{Etc.getgrnam("tty").gid},mode=0620,ptmxmode=0666,newinstance" },
18
+ ]
19
+ end
20
+
21
+ def real_path(path)
22
+ File.join(root_dir, path)
23
+ end
24
+
25
+ def init
26
+ setup_dirs
27
+ setup_files
28
+ setup_devs
29
+ mount_all
30
+ true
31
+ end
32
+
33
+ def install(*pkgs)
34
+ yum_install(*pkgs)
35
+ rpm_erase_exclude(*pkgs)
36
+ end
37
+
38
+ def installed_pkgs
39
+ rpm_qa
40
+ end
41
+
42
+ def execute(*cmd)
43
+ chroot_run_cmd(*cmd)
44
+ end
45
+
46
+ def clean
47
+ umount_all
48
+ FileUtils.remove_entry_secure root_dir
49
+ end
50
+
51
+ def list
52
+ rpm_qa
53
+ end
54
+
55
+ private
56
+
57
+ def yum_install(*pkgs)
58
+ cmd = ["yum", "--installroot", root_dir, "install"] + pkgs
59
+ run_cmd(*cmd)
60
+ end
61
+
62
+ def rpm_erase_exclude(*pkgs)
63
+ remove_pkgs = gratuitous_pkgs(*pkgs)
64
+ if remove_pkgs.size > 0
65
+ cmd = ["rpm", "--root", root_dir, "--erase"] + remove_pkgs
66
+ run_cmd(*cmd)
67
+ end
68
+ end
69
+
70
+ def gratuitous_pkgs(*pkgs)
71
+ rpm_qa - pkgs
72
+ end
73
+
74
+ def rpm_qa
75
+ run_cmd_capture_stdout("rpm", "--root", root_dir, "-qa").sort
76
+ end
77
+
78
+ def run_cmd(*cmd)
79
+ ret = Kernel.system(*cmd)
80
+ raise "command failed(ret: #{ret}) '#{cmd.join(" ")}'" unless ret
81
+ end
82
+
83
+ def chroot_run_cmd(*cmd)
84
+ run_cmd("chroot", root_dir, *cmd)
85
+ end
86
+
87
+ def run_cmd_capture_stdout(*cmd)
88
+ Open3.popen3(*cmd) do |stdin, stdout, stderr|
89
+ stdin.close
90
+ stdout.readlines.map { |l| l.chomp }
91
+ end
92
+ end
93
+
94
+ def write_file(path, content)
95
+ open(real_path(path), "w") { |f| f.write content }
96
+ end
97
+
98
+ def mount_all
99
+ @mounts.each do |entry|
100
+ mount(entry)
101
+ end
102
+ end
103
+
104
+ def mount(entry)
105
+ unless mounted?(entry)
106
+ cmd = ["mount", "-t", entry[:type]]
107
+ cmd.concat ["-o", entry[:options]] if entry.has_key?(:options)
108
+ cmd.concat ["pandler_mount", real_path(entry[:path])]
109
+ run_cmd(*cmd)
110
+ end
111
+ end
112
+
113
+ def mounted?(entry)
114
+ `mount`.split("\n").map { |line| line.split[2] }.include?(real_path(entry[:path]))
115
+ end
116
+
117
+ def umount_all
118
+ @mounts.each do |entry|
119
+ umount(entry)
120
+ end
121
+ end
122
+
123
+ def umount(entry)
124
+ if mounted?(entry)
125
+ cmd = ["umount", "-l", real_path(entry[:path])]
126
+ run_cmd(*cmd)
127
+ end
128
+ end
129
+
130
+ def setup_dirs
131
+ FileUtils.mkdir_p root_dir
132
+ dirs = [
133
+ '/var/lib/rpm',
134
+ '/var/lib/yum',
135
+ '/var/lib/dbus',
136
+ '/var/log',
137
+ '/var/lock/rpm',
138
+ '/var/cache/yum',
139
+ '/etc/rpm',
140
+ '/tmp',
141
+ '/tmp/ccache',
142
+ '/var/tmp',
143
+ '/etc/yum.repos.d',
144
+ '/etc/yum',
145
+ '/proc',
146
+ '/sys',
147
+ ]
148
+ dirs.each do |dir|
149
+ FileUtils.mkdir_p real_path(dir)
150
+ end
151
+ end
152
+
153
+ def setup_files
154
+ files = [
155
+ '/etc/mtab',
156
+ '/etc/fstab',
157
+ '/var/log/yum.log',
158
+ ]
159
+ files.each do |file|
160
+ FileUtils.touch real_path(file)
161
+ end
162
+
163
+ write_file("/etc/yum/yum.conf", yum_conf)
164
+ FileUtils.ln_s("yum/yum.conf", real_path("/etc/yum.conf"), { :force => true })
165
+ FileUtils.cp("/etc/resolv.conf", real_path("/etc/resolv.conf"))
166
+ FileUtils.cp("/etc/hosts", real_path("/etc/hosts"))
167
+ end
168
+
169
+ def setup_devs
170
+ FileUtils.mkdir_p real_path("/dev/pts")
171
+ FileUtils.mkdir_p real_path("/dev/shm")
172
+
173
+ python_code = <<-PYTHON
174
+ import sys, os, os.path, stat
175
+ devFiles = [
176
+ (stat.S_IFCHR | 0666, os.makedev(1, 3), "#{real_path("/dev/null")}"),
177
+ (stat.S_IFCHR | 0666, os.makedev(1, 7), "#{real_path("/dev/full")}"),
178
+ (stat.S_IFCHR | 0666, os.makedev(1, 5), "#{real_path("/dev/zero")}"),
179
+ (stat.S_IFCHR | 0666, os.makedev(1, 8), "#{real_path("/dev/random")}"),
180
+ (stat.S_IFCHR | 0444, os.makedev(1, 9), "#{real_path("/dev/urandom")}"),
181
+ (stat.S_IFCHR | 0666, os.makedev(5, 0), "#{real_path("/dev/tty")}"),
182
+ (stat.S_IFCHR | 0600, os.makedev(5, 1), "#{real_path("/dev/console")}"),
183
+ # (stat.S_IFCHR | 0666, os.makedev(5, 2), "#{real_path("/dev/ptmx")}"),
184
+ ]
185
+ for i in devFiles:
186
+ if os.path.exists(i[2]):
187
+ continue
188
+ else:
189
+ os.mknod(i[2], i[0], i[1])
190
+ sys.exit(0)
191
+ PYTHON
192
+ run_cmd("python", "-c", python_code)
193
+
194
+ FileUtils.symlink("/proc/self/fd/0", real_path("/dev/stdin"), { :force => true })
195
+ FileUtils.symlink("/proc/self/fd/1", real_path("/dev/stdout"), { :force => true })
196
+ FileUtils.symlink("/proc/self/fd/2", real_path("/dev/stderr"), { :force => true })
197
+
198
+ FileUtils.chown(Etc.getpwnam("root").uid, Etc.getgrnam("tty").gid, real_path("/dev/tty"))
199
+ # FileUtils.chown(Etc.getpwnam("root").uid, Etc.getgrnam("tty").gid, real_path("/dev/ptmx"))
200
+
201
+ unless File.exists? real_path("/dev/fd")
202
+ FileUtils.symlink("/proc/self/fd", real_path("/dev/fd"), { :force => true })
203
+ end
204
+ FileUtils.symlink("pts/ptmx", real_path("/dev/ptmx"), { :force => true })
205
+ end
206
+
207
+ def yum_conf
208
+ content = <<-EOF
209
+ [main]
210
+ cachedir=/var/cache/yum
211
+ debuglevel=1
212
+ reposdir=/dev/null
213
+ logfile=/var/log/yum.log
214
+ retries=20
215
+ obsoletes=1
216
+ gpgcheck=0
217
+ assumeyes=1
218
+ syslog_ident=pandler
219
+ syslog_device=
220
+ plugins=0
221
+
222
+ [pandler]
223
+ name=Pandler
224
+ enabled=1
225
+ baseurl=#{yumrepo}
226
+ EOF
227
+ content
228
+ end
229
+ end