]> git.proxmox.com Git - dab.git/blob - dab
jammy: add future ubuntu 22.04 jammy release
[dab.git] / dab
1 #!/usr/bin/perl -w
2
3 use strict;
4 use warnings;
5
6 use Getopt::Long;
7
8 use PVE::DAB;
9
10 $ENV{'LC_ALL'} = 'C';
11
12 my $commands = {
13 'init' => '',
14 'bootstrap' => '[--exim] [--include <a[,b..]]>] --exclude [<a[,b..]]>] [--minimal]',
15 'finalize' => '[--keepmycnf] [--compressor <gz[ip] (default)|zst[d]|zstd-max>]',
16 'veid' => '',
17 'basedir' => '',
18 'packagefile' => '',
19 'list' => '[--verbose]',
20 'task' => '<postgres|mysql|php> [--version] [--password] [--memlimit]',
21 'install' => '<package or *.pkglist file> ...',
22 'exec' => '<cmd> ...',
23 'enter' => '',
24 'clean' => '',
25 'dist-clean' => '',
26 'help' => '',
27 };
28
29 sub print_usage {
30 print STDERR "USAGE: dab <command> [parameters]\n\n";
31
32 for my $cmd (sort keys %$commands) {
33 if (my $opts = $commands->{$cmd}) {
34 print STDERR " dab $cmd $opts\n";
35 } else {
36 print STDERR " dab $cmd\n";
37 }
38 }
39 }
40
41 sub fatal_usage {
42 my ($msg) = @_;
43
44 print STDERR "\nERROR: $msg\n\n" if $msg;
45 print_usage();
46
47 exit (-1);
48 }
49
50 if (scalar (@ARGV) == 0) {
51 fatal_usage("no command specified");
52 }
53
54 my $cmdline = join (' ', @ARGV);
55 my $cmd = shift @ARGV;
56
57 if (!$cmd) {
58 fatal_usage("no command specified");
59 } elsif (!exists $commands->{$cmd}) {
60 fatal_usage("unknown command '$cmd'");
61 } elsif ($cmd eq 'help') {
62 print_usage();
63 exit (0);
64 }
65
66 my $dab;
67 sub dab() {
68 $dab = PVE::DAB->new() if !$dab;
69 return $dab;
70 }
71
72 dab->writelog ("dab: $cmdline\n");
73
74 $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
75 die "interrupted by signal\n";
76 };
77
78 eval {
79 if ($cmd eq 'init') {
80 die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
81
82 dab->initialize();
83
84 } elsif ($cmd eq 'bootstrap') {
85 my $opts = {};
86 if (!GetOptions ($opts, 'exim', 'minimal', 'include=s', 'exclude=s')) {
87 fatal_usage();
88 }
89 die "command 'bootstrap' expects no arguments.\n" if scalar (@ARGV) != 0;
90
91 $dab->ve_init();
92 $dab->bootstrap ($opts);
93
94 } elsif ($cmd eq 'finalize') {
95 my $opts = {};
96 if (!GetOptions ($opts, 'keepmycnf', 'compressor=s')) {
97 fatal_usage();
98 }
99 die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
100
101 $dab->finalize($opts);
102
103 } elsif ($cmd eq 'veid') {
104 die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
105
106 print $dab->{veid} . "\n";
107
108 } elsif ($cmd eq 'basedir') {
109 die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
110
111 print $dab->{rootfs} . "\n";
112
113 } elsif ($cmd eq 'packagefile') {
114 die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
115
116 print "$dab->{targetname}.tar.gz\n";
117
118 } elsif ($cmd eq 'list') {
119 my $verbose;
120 if (!GetOptions ('verbose' =>\$verbose)) {
121 fatal_usage();
122 }
123 die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
124
125 my $instpkgs = $dab->read_installed ();
126
127 foreach my $pkg (sort keys %$instpkgs) {
128 if ($verbose) {
129 my $version = $instpkgs->{$pkg}->{version};
130 print "$pkg $version\n";
131 } else {
132 print "$pkg\n";
133 }
134 }
135
136 } elsif ($cmd eq 'task') {
137 my $task = shift @ARGV;
138 if (!$task) {
139 fatal_usage("no task specified");
140 }
141
142 my $opts = {};
143 if ($task eq 'mysql') {
144 if (!GetOptions ($opts, 'password=s', 'start')) {
145 fatal_usage();
146 }
147 die "task '$task' expects no arguments.\n" if scalar (@ARGV) != 0;
148
149 $dab->task_mysql ($opts);
150
151 } elsif ($task eq 'postgres') {
152 if (!GetOptions ($opts, 'version=s', 'start')) {
153 fatal_usage();
154 }
155 die "task '$task' expects no arguments.\n" if scalar (@ARGV) != 0;
156
157 $dab->task_postgres ($opts);
158
159 } elsif ($task eq 'php') {
160 if (!GetOptions ($opts, 'memlimit=i')) {
161 fatal_usage();
162 }
163 die "task '$task' expects no arguments.\n" if scalar (@ARGV) != 0;
164
165 $dab->task_php ($opts);
166
167 } else {
168 fatal_usage("unknown task '$task'");
169 }
170
171 } elsif ($cmd eq 'install' || $cmd eq 'unpack') {
172 my $required;
173 foreach my $arg (@ARGV) {
174 if ($arg =~ m/\.pkglist$/) {
175 open (TMP, $arg) ||
176 die "cant open package list '$arg' - $!";
177 while (defined (my $line = <TMP>)) {
178 chomp $line;
179 next if $line =~ m/^\s*$/;
180 next if $line =~ m/\#/;
181 if ($line =~ m/^\s*(\S+)\s*$/) {
182 push @$required, $1;
183 } else {
184 die "invalid package name in '$arg' - $line\n";
185 }
186 }
187 } else {
188 push @$required, $arg;
189 }
190
191 close (TMP);
192 }
193
194 $dab->install ($required, $cmd eq 'unpack');
195
196 } elsif ($cmd eq 'exec') {
197
198 $dab->ve_exec (@ARGV);
199
200 } elsif ($cmd eq 'enter') {
201 die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
202
203 $dab->enter();
204
205 } elsif ($cmd eq 'clean') {
206 die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
207
208 $dab->cleanup(0);
209
210 } elsif ($cmd eq 'dist-clean') {
211 die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0;
212
213 $dab->cleanup(1);
214
215 } else {
216 fatal_usage("invalid command '$cmd'");
217 }
218
219 };
220 if (my $err = $@) {
221 $dab->logmsg ($@);
222 die ($@);
223 }
224
225 exit 0;
226
227 __END__
228
229 =head1 NAME
230
231 dab - Debian LXC Appliance Builder
232
233 =head1 SYNOPSIS
234
235 =over
236
237 =item B<dab> I<command> I<[OPTIONS]>
238
239 =item B<dab init>
240
241 Downloads the package descriptions form the repository. Also truncates the
242 C<logfile>.
243
244 =item B<dab bootstrap>
245
246 Bootstrap a debian system and allocate a temporary container (we use IDs 90000
247 and above).
248
249 =over
250
251 =item I<--exim>
252
253 Use exim as MTA (dab selects postfix by default)
254
255 =item I<--minimal>
256
257 Do not auto-select packages with standard priority for installation.
258
259 =item I<--include <a[,b..]]>
260
261 A comma-separated list of packages to always include in bootstrap. Note that no
262 transitive dependency resolution is done, you may need to specify those
263 yourself.
264
265 =item I<--exclude <a[,b..]]>
266
267 A comma-separated list of packages to exlcude in bootstrap. Note that no
268 transitive dependency resolution is done for others to get excluded, you may
269 need to specify those yourself.
270
271 =back
272
273 =item B<dab veid>
274
275 Print used container ID.
276
277 =item B<dab basedir>
278
279 Print container private directory.
280
281 =item B<dab packagefile>
282
283 Print the appliance file name.
284
285 =item B<dab install I<pkg ...>>
286
287 Install one or more packages. I<pkg> can also refer to a file named
288 C<xyz.pkglist> which contains a list of packages. All dependencies are
289 automatically installed.
290
291 =item B<dab unpack I<pkg ...>>
292
293 Unpack one or more packages. I<pkg> can also refer to a file named
294 C<xyz.pkglist> which contains a list of packages. All dependencies are
295 automatically unpacked.
296
297 =item B<dab exec I<CMD> I<ARGS>>
298
299 Executes command CMD inside the container.
300
301 =item B<dab enter>
302
303 Calls C<lxc-attach> - this is for debugging only.
304
305 =item B<dab task mysql>
306
307 Install a mysql database server. During appliance generation we use C<admin> as
308 mysql root password (also stored in /root/.my.cnf).
309
310 =over
311
312 =item I<--password=XXX>
313
314 Specify the mysql root password. The special value C<random> can be use to
315 generate a random root password when the appliance is started first time
316 (stored in /root/.my.cnf)
317
318 =item I<--start>
319
320 Start the mysql server (if you want to execute sql commands during
321 appliance generation).
322
323 =back
324
325 =item B<dab task postgres>
326
327 Install a postgres database server.
328
329 =over
330
331 =item I<--version=XXX>
332
333 Select Postgres version. Posible values are for example C<9.6>, C<11> or C<13>,
334 they depend on the selected distribution suite. Defaults to none, which selects
335 the unversioned metapackage that pulls in the suites default version, normally
336 a good choice to make.
337
338 =item I<--start>
339
340 Start the postgres server immediately. Useful, for example, if you want to
341 execute sql commands during appliance generation.
342
343 =back
344
345 =item B<dab task php>
346
347 Install php5.
348
349 =over
350
351 =item I<--memlimit=i>
352
353 Set the php I<memory_limit>.
354
355 =back
356
357 =item B<dab finalize>
358
359 Cleanup everything inside the container and generate the final appliance
360 package.
361
362 =over
363
364 =item I<--keepmycnf>
365
366 Do not delete file C</root/.my.cfg> (mysql).
367
368 =item I<--compressor <gz[ip] (default)|zst[d]|zstd-max>]>
369
370 Select the compressor to process the rootfs archive with. C<gzip> is a good
371 choice to make the archive also available on older systems, but using C<zstd>
372 or even C<zstd-max> results in a higher compression ration while keeping
373 decompression very fast and highly efficient. Note that C<zstd-max> uses the
374 highest compression ratio without any decompression performance hit possible,
375 it will use as many threads as there are onlince CPU threads and may thus
376 increase the system load significantly for tens of seconds up to minutes.
377
378 =back
379
380 =item B<dab list>
381
382 List installed packages.
383
384 =over
385
386 =item I<--verbose>
387
388 Also print package versions.
389
390 =back
391
392 =item B<dab clean>
393
394 Remove all temporary files and destroy the container.
395
396 =item B<dab dist-clean>
397
398 Like clean, but also removes the package cache (except when you specified your
399 own cache directory in the config file)
400
401 =back
402
403 =head1 DESCRIPTION
404
405 dab is a script to automate the creation of LXC appliances. It is basically a
406 rewrite of debootstrap in perl, but uses LXC instead of chroot and generates
407 LXC templates. Another difference is that it supports multi-stage building of
408 templates. That way you can execute arbitrary scripts between to accomplish
409 what you want.
410
411 Furthermore some common tasks are fully automated, like setting up a database
412 server (mysql or postgres).
413
414 To accomplish minimal template creation time, packages are cached to a local
415 directory, so you do not need a local debian mirror (although this would speed
416 up the first run).
417
418 See http://pve.proxmox.com/wiki/Debian_Appliance_Builder for examples.
419
420 This script need to be run as root, so it is not recommended to start it on a
421 production machine with running containers. So many people run Proxmox VE
422 inside a KVM or VMWare 64bit virtual machine to build appliances.
423
424 All generated templates includes an appliance description file. Those can be
425 used to build appliance repositories.
426
427 =head1 CONFIGURATION
428
429 Configuration is read from the file C<dab.conf> inside the current working
430 directory. The files contains key value pairs, separated by colon.
431
432 =over 2
433
434 =item B<Suite:> I<squeeze|wheezy|jessie|trusty|vivid>
435
436 The Debian or Ubuntu suite.
437
438 =item B<Source:> I<URL [components]>
439
440 Defines a source location. By default we use the following for debian:
441
442 Source: http://ftp.debian.org/debian SUITE main contrib
443 Source: http://security.debian.org SUITE/updates main contrib
444
445 Note: SUITE is a variable and will be substituted.
446
447 There are also reasonable defaults for Ubuntu. If you do not specify any source
448 the defaults are used.
449
450 =item B<Depends:> I<dependencies>
451
452 Debian like package dependencies. This can be used to make sure that speific
453 package versions are available.
454
455 =item B<CacheDir>: I<path>
456
457 Allows you to specify the directory where downloaded packages are cached.
458
459 =item B<Mirror:> I<SRCURL> => I<DSTURL>
460
461 Define a mirror location. for example:
462
463 Mirror: http://ftp.debian.org/debian => ftp://mirror/debian
464
465 =back
466
467 All other settings in this files are also included into the appliance
468 description file.
469
470 =over 2
471
472 =item B<Name:> I<name>
473
474 The name of the appliance.
475
476 Appliance names must consist only of lower case letters (a-z), digits (0-9),
477 plus (+) and minus (-) signs, and periods (.). They must be at least two
478 characters long and must start with an alphanumeric character.
479
480 =item B<Architecture:> I<i386|amd64>
481
482 Target architecture.
483
484 =item B<Version:> I<upstream_version[-build_revision]>
485
486 The version number of an appliance.
487
488 =item: B<Section:> I<section>
489
490 This field specifies an application area into which the appliance has been
491 classified. Currently we use the following section names: system, mail
492
493 =item B<Maintainer:> I<name <email>>
494
495 The appliance maintainer's name and email address. The name should come first,
496 then the email address inside angle brackets <> (in RFC822 format).
497
498 =item B<Infopage:> I<URL>
499
500 Link to web page containing more informations about this appliance.
501
502 =item B<Description:> I<single line synopsis>
503
504 extended description over several lines (indended by space) may follow.
505
506 =back
507
508 =head1 Appliance description file
509
510 All generated templates includes an appliance description file called
511
512 /etc/appliance.info
513
514 this is the first file inside the tar archive. That way it can be easily
515 exctracted without scanning the whole archive. The file itself contains
516 informations like a debian C<control> file. It can be used to build appliance
517 repositories.
518
519 Most fields are directly copied from the configuration file C<dab.conf>.
520
521 Additionally there are some auto-generated files:
522
523 =over
524
525 =item B<Installed-Size:> I<bytes>
526
527 It gives the total amount of disk space required to install the named
528 appliance. The disk space is represented in megabytes as a simple decimal
529 number.
530
531 =item B<Type:> I<type>
532
533 This is always C<lxc>.
534
535 =item B<OS:> I<[debian-4.0|debian-5.0|ubuntu-8.0]>
536
537 Operation system.
538
539 =back
540
541 Appliance repositories usually add additional fields:
542
543 =over
544
545 =item B<md5sum:> I<md5sum>
546
547 MD5 checksum
548
549 =back
550
551 =head1 FILES
552
553 The following files are created inside your working directory:
554
555 dab.conf appliance configuration file
556
557 logfile contains installation logs
558
559 .veid stores the used container ID
560
561 cache/* default package cache directory
562
563 info/* package information cache
564
565 =head1 AUTHOR
566
567 Dietmar Maurer <dietmar@proxmox.com>
568 Thomas Lamprecht <t.lamprecht@proxmox.com>
569
570 Many thanks to Proxmox Server Solutions (www.proxmox.com) for sponsoring this
571 work.
572
573 =head1 COPYRIGHT AND DISCLAIMER
574
575 Copyright (C) 2007-2021 Proxmox Server Solutions GmbH
576
577 Copyright: dab is under GNU GPL, the GNU General Public License.
578
579 This program is free software; you can redistribute it and/or modify
580 it under the terms of the GNU General Public License as published by
581 the Free Software Foundation; version 2 dated June, 1991.
582
583 This program is distributed in the hope that it will be useful,
584 but WITHOUT ANY WARRANTY; without even the implied warranty of
585 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
586 GNU General Public License for more details.
587
588 You should have received a copy of the GNU General Public License
589 along with this program; if not, write to the
590 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
591 MA 02110-1301, USA.