Container-Builder

 view release on metacpan or  search on metacpan

lib/Container/Builder.pm  view on Meta::CPAN

	field $os_version :param = 'bookworm';
	field @layers :reader(get_layers);
	field $original_dir = Cwd::getcwd();
	field $runas = 'root';
	field $work_dir = '/';
	field @entry :reader(get_entry);
	field @cmd :reader(get_cmd);
	field %env :reader(get_env);
	field %deb_packages;
	field @dirs :reader(get_dirs);
	field @users :reader(get_users);
	field @groups :reader(get_groups);
	field $packages; 

	# Podman will use the Container::Builder::Config digest as the identifier for your imported container. Users might want to have this ID.
	field $ctr_digest = undef;

	method _parse_packages(@fields) {
		if($enable_packages_cache && !-r $packages_file) { # Our cache file does not exist
			$debian_pkg_hostname =~ s/[^\w\-\.]//g; # a light scrubbing on the hostname... But we still assume the caller does the scrubbing!
			my $packagesgz = LWP::Simple::get("https://$debian_pkg_hostname/debian/dists/$os_version/main/binary-amd64/Packages.gz");
			IO::Uncompress::Gunzip::gunzip(\$packagesgz => $packages_file);
			$packages = DPKG::Packages::Parser->new('file' => $packages_file);
		} elsif($enable_packages_cache) { # Our cache file exists
			$packages = DPKG::Packages::Parser->new('file' => $packages_file);
		} elsif(!$enable_packages_cache) { # don't drop anything to disk
			$debian_pkg_hostname =~ s/[^\w\-\.]//g; # a light scrubbing on the hostname... But we still assume the caller does the scrubbing!
			my $packagesgz = LWP::Simple::get("https://$debian_pkg_hostname/debian/dists/$os_version/main/binary-amd64/Packages.gz");
			my $packages_raw;
			IO::Uncompress::Gunzip::gunzip(\$packagesgz => \$packages_raw);
			open(my $f, '<', \$packages_raw);
			$packages = DPKG::Packages::Parser->new(fh => $f);
		}
		$packages->parse(@fields);
	}

	method _get_deb_package($package_name) {
		if($cache_folder) {
			my $cache_folder_ws = $cache_folder . (substr($cache_folder, -1) eq '/' ? '' : '/'); # ws = with slash
			if(-d $cache_folder_ws && -r $cache_folder_ws . $package_name . '.deb') {
				local $/ = undef;
				open(my $deb, '<', $cache_folder_ws . $package_name . '.deb') or die "Cannot open $cache_folder_ws$package_name.deb\n";
				my $deb_content = <$deb>;
				close($deb);
				return $deb_content;
			}
		}

		$self->_parse_packages('Filename', 'Depends') if !$packages; # lazy load on first call
		my $pkg = $packages->get_package($package_name);
		return 0 if !$pkg;

		my $filepath = $pkg->{Filename};
		my ($filename) = $filepath =~ m/([^\/]+)$/;
		my $url = "https://debian.inf.tu-dresden.de/debian/" . $filepath;
		my $lwp = LWP::UserAgent->new();
		my $response = $lwp->get($url);
		if(!$response->is_success) { # Added because my base perl LWP didn't have the https package to support https...
			die "Call to Debian package repo failed: " . $response->status_line;
		}
		my $package_content = $response->decoded_content;
		die "unable to get package content with LWP::Simple" if !$package_content;
		return $package_content;
	}

	method add_deb_package($package_name) {
		return 0 if $deb_packages{$package_name};
		my $package_content = $self->_get_deb_package($package_name);
		return 0 if ! $package_content;

		if($cache_folder) {
			my $cache_folder_ws = $cache_folder . (substr($cache_folder, -1) eq '/' ? '' : '/'); # ws = with slash
			if(!-r $cache_folder_ws . $package_name . '.deb') {
				open(my $f, '>', $cache_folder_ws . $package_name . '.deb') or die "cannot open $cache_folder_ws$package_name.deb\n";
				print $f $package_content;
				close($f);
			}
		}

		# Before adding the package as a layer, get the dependencies and add those
		$deb_packages{$package_name} = 1;
		$self->_parse_packages('Filename', 'Depends') if !$packages; # lazy load on first call
		my $pkg = $packages->get_package($package_name);
		foreach(@{$pkg->{Depends}}) {
			if(ref eq 'ARRAY') {
				# TODO: there's no way we can make an intelligent decision here, we can check if any of these have already been added or not. If one of the options was already added, we can skip choosing; if none was already added, take the first one.
				$self->add_deb_package(${$_}[0]->{name});
			} elsif(ref eq 'HASH') {
				$self->add_deb_package($_->{name});
			}
		}

		push @layers, Container::Builder::Layer::DebianPackageFile->new(comment => $package_name, data => $package_content, compress => $compress_deb_tar);
	}

	# Create a layer that adds a package to the container
	method add_deb_package_from_file($filepath_deb) {
		die "Unable to read $filepath_deb\n" if !-r $filepath_deb;
		push @layers, Container::Builder::Layer::DebianPackageFile->new(comment => $filepath_deb, file => $filepath_deb, compress => $compress_deb_tar);
	}

	method extract_from_deb($package_name, $files_to_extract) {
		my $deb_archive = $self->_get_deb_package($package_name);
		if($deb_archive) {
			# Read the tar -> with our own class because Archive::Tar doesn't read from a string...
			my $deb = Container::Builder::Layer::DebianPackageFile->new(comment => $package_name, data => $deb_archive, compress => $compress_deb_tar);
			my $tar = $deb->generate_artifact();
			my $tar_builder = Container::Builder::Tar->new();
			my $result_tar = '';
			foreach(@$files_to_extract) {
				my $tar_file = '';
				if($_ =~ /\*$/) {
					$tar_file = $tar_builder->extract_wildcard_files($tar, $_);
				} else {
					$tar_file = $tar_builder->extract_file($tar, $_);
				}
				$result_tar .= $tar_file;
			}
			$result_tar .= "\x00" x 1024; # two empty blocks
			push @layers, Container::Builder::Layer::Tar->new(comment => "custom $package_name", data => $result_tar);
		} else {



( run in 1.712 second using v1.01-cache-2.11-cpan-2398b32b56e )