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