Container-Builder
view release on metacpan or search on metacpan
If $location_in_ctr has a slash at the end, the last directory of
$local_dirpath will become a subdirectory of the path
$location_in_ctr. Otherwise, the last directory of $local_dirpath
will be renamed to the last directory of $location_in_ctr.
For example copy('lib/', '/app/') will create /app/lib/ but
copy('lib/', '/app') will put all put the files and directories
directly inside /app, there will be no lib directory.
create_directory($path, $mode, $uid, $gid)
Create an empty directory at $path inside the container with the
specified $mode, $user and $group.
add_user($name, $uid, $main_gid, $shell, $homedir)
Add a user to the container. This puts the user inside the
/etc/passwd file.
add_group($name, $gid)
Add a group to the container. This puts the group inside the
/etc/group file.
runas_user($user)
Specify the user to run the entrypoint as.
set_env($key, $value)
examples/fatpacked.plackup view on Meta::CPAN
C<HTTP_AUTHORIZATION> environment variable by default for security
reasons. Authentication middleware such as L<Plack::Middleware::Auth::Basic> or
L<Catalyst::Authentication::Credential::HTTP> requires the variable to
be set up. Plack::Handler::FCGI supports extracting the C<Authorization> environment
variable when it is configured that way.
Apache2 with mod_fastcgi:
--pass-header Authorization
mod_fcgid:
FcgidPassHeader Authorization
=head2 Server::Starter
This plack handler supports L<Server::Starter> as a superdaemon.
Simply launch plackup from start_server with a path option.
The listen option is ignored when launched from Server::Starter.
start_server --path=/tmp/socket -- plackup -s FCGI app.psgi
=head1 SEE ALSO
lib/Container/Builder.pm view on Meta::CPAN
local $/ = undef;
open(my $file, '<', $item) or die "cannot open file $item for reading\n";
my $data = <$file>;
$tar->add_file($remote_file, $data, $mode, $user, $group);
}
}
push @layers, Container::Builder::Layer::Tar->new(comment => $local_dirpath, data => $tar->get_tar());
}
# Create a layer that creates a directory in the container
method create_directory($path, $mode, $uid, $gid) {
my %dir = (path => $path, mode => $mode, uid => $uid, gid => $gid);
push @dirs, \%dir;
}
# Create a layer that adds a user to the container
# this is a wrapper to make a change to passwd?
method add_user($name, $uid, $main_gid, $shell, $homedir) {
$name =~ s/[^a-z]//ig;
$uid =~ s/[^\d]//g;
$main_gid =~ s/[^\d]//g;
die "Conflicting user" if grep { $_->{name} eq $name || $_->{uid} == $uid || $_->{gid} == $main_gid } @users;
my %new_user = (name => $name, uid => $uid, gid => $main_gid, shell => $shell, homedir => $homedir);
push @users, \%new_user;
}
# Create a layer that adds a group to the container
method add_group($name, $gid) {
$name =~ s/[^a-z]//ig;
$gid =~ s/[^\d]//g;
die "Conflicting with existing group\n" if grep {$_->{name} eq $name || $_->{gid} == $gid } @groups;
my %new_group = (name => $name, gid => $gid);
push @groups, \%new_group;
}
# similar to USER in Dockerfile
method runas_user($user) {
my $found_user = 0;
foreach(@users) {
$found_user = 1 if $_->{name} eq $user;
}
die "Cannot set the USER to $user if it's not part of the users in the container\n" if !$found_user;
lib/Container/Builder.pm view on Meta::CPAN
$tag_name = $_[1];
}
} elsif(@_ == 1) { # Backwards compatibility
$filename_result = $_[0];
}
# Make 1 layer with all the base files
my $tar = Container::Builder::Tar->new();
foreach(@dirs) {
$tar->add_dir($_->{path}, $_->{mode}, $_->{uid}, $_->{gid});
}
# Generate /etc/group file
my $etcgroup = '';
map { $etcgroup .= $_->{name} . ':x:' . $_->{gid} . ':' . $/ } @groups;
$tar->add_file('/etc/group', $etcgroup, 0644, 0, 0);
# Generate /etc/passwd file
my $etcpasswd = '';
# example line: root:x:0:0:root:/root:/bin/bash
map { $etcpasswd .= $_->{name} . ':x:' . $_->{uid} . ':' . $_->{gid} . ':' . $_->{name} . ':' . $_->{homedir} . ':' . $_->{shell} . $/ } @users;
$tar->add_file('/etc/passwd', $etcpasswd, 0644, 0, 0);
my $tar_content = $tar->get_tar();
unshift @layers, Container::Builder::Layer::Tar->new(comment => 'Base files', data => $tar_content);
$tar = Container::Builder::Tar->new();
$tar->add_dir('blobs/', 0755, 0, 0);
$tar->add_dir('blobs/sha256/', 0755, 0, 0);
# Add all layers
foreach(@layers) {
lib/Container/Builder.pm view on Meta::CPAN
C<$compressed> is a boolean to determine if the image layer will be compressed.
=item copy($local_dirpath, $location_in_ctr, $mode, $user, $group)
Recursively copy the C<$local_dirpath> directory into a layer of the container. The resulting path inside the container is defined by C<$location_in_ctr>. C<$mode> controls the directory permission of C<$location_in_ctr> only. Inner directories will ...
If C<$location_in_ctr> has a slash at the end, the last directory of C<$local_dirpath> will become a subdirectory of the path C<$location_in_ctr>. Otherwise, the last directory of C<$local_dirpath> will be renamed to the last directory of C<$location...
For example C<copy('lib/', '/app/')> will create C</app/lib/> but C<copy('lib/', '/app')> will put all put the files and directories directly inside C</app>, there will be no C<lib> directory.
=item create_directory($path, $mode, $uid, $gid)
Create an empty directory at C<$path> inside the container with the specified C<$mode>, C<$user> and C<$group>.
=item add_user($name, $uid, $main_gid, $shell, $homedir)
Add a user to the container. This puts the user inside the C</etc/passwd> file.
=item add_group($name, $gid)
Add a group to the container. This puts the group inside the C</etc/group> file.
=item runas_user($user)
Specify the user to run the entrypoint as.
=item set_env($key, $value)
Add a environment variable to the container definition.
lib/Container/Builder/Layer/Directory.pm view on Meta::CPAN
use Container::Builder::Layer;
use Container::Builder::Tar;
use Crypt::Digest::SHA256 qw(sha256_hex);
class Container::Builder::Layer::Directory :isa(Container::Builder::Layer) {
field $path :param;
field $mode :param;
field $uid :param;
field $gid :param;
method generate_artifact() {
my $tar = Container::Builder::Tar->new();
$tar->add_dir($path, $mode, $uid, $gid);
my $tar_content = $tar->get_tar();
my $d = $self->_parent_does_stuff(\$tar_content);
return $$d;
}
}
1;
__END__
=encoding utf-8
lib/Container/Builder/Layer/Directory.pm view on Meta::CPAN
Container::Builder::Layer::Directory - Make a container layer consisting of a single directory.
=head1 DESCRIPTION
Container::Builder::Layer::Directory implements Container::Builder::Layer and can be used to create container layers which contain a single directory.
=head1 METHODS
=over 1
=item new(path => '/', mode => 0755, uid => 1337, gid => 1337)
Create a C<Container::Builder::Layer::Directory> object. As arguments pass in the details of the directory that you want to have in your container layer.
=item generate_artifact()
Returns a TAR file as a scalar, that contains a single directory.
=item get_media_type()
Return the media type of the container. This is the mime type of the layer. Possibilities are C<application/vnd.oci.image.layer.v1.tar> or C<application/vnd.oci.image.layer.v1.tar+gzip>.
lib/Container/Builder/Tar.pm view on Meta::CPAN
# So here we are making TAR files from scratch...
class Container::Builder::Tar {
field $full_tar = '';
method get_tar() {
my $str = $full_tar;
$str .= "\x00" x 1024; # 2 empty blocks at the end
return $str;
}
method add_dir($path, $mode, $uid, $gid) {
$path = '.' . $path if $path =~ /^\//;
die "Path is longer than 98 chars" if length($path) > 98;
die "Mode is too long" if length(sprintf("%07o", int($mode))) > 7;
die "Uid is too long" if length(sprintf("%07o", int($uid))) > 7;
die "Gid is too long" if length(sprintf("%07o", int($gid))) > 7;
my $tar = $path . "\x00" x (100-length($path)); # char name[100]; /* 0 */
$tar .= sprintf("%07o", int($mode)) . "\x00"; # char mode[8]; /* 100 */
$tar .= sprintf("%07o", int($uid)) . "\x00"; # char uid[8]; /* 108 */ -> not sure why we use 6 bytes but that's how other tars do it
$tar .= sprintf("%07o", int($gid)) . "\x00"; # char gid[8]; /* 116 */ -> not sure why we use 6 bytes but that's how other tars do it
$tar .= sprintf("%011o", 0) . "\x00"; # char size[12]; /* 124 */ -> dir size is always 0...
$tar .= sprintf("%011o", 566833020) . "\x00"; # char mtime[12]; /* 136 */
$tar .= "\x20" x 8; # char chksum[8]; /* 148 */ -> we'll do this later
$tar .= "5"; # char typeflag; /* 156 */ --> A dir is 5
$tar .= "\x00" x 100; # char linkname[100]; /* 157 */
$tar .= "ustar\x00"; # char magic[6]; /* 257 */
$tar .= "00"; # char version[2]; /* 263 */
#$tar .= "ustar\x20\x20\x00";
$tar .= "\x00" x 32; # char uname[32]; /* 265 */
$tar .= "\x00" x 32; # char gname[32]; /* 297 */
lib/Container/Builder/Tar.pm view on Meta::CPAN
# When I followed the spec to a tee, it gave errors. When I do the same and make a 6 byte number (no zeroes in front) + null byte + left over space (\x20), it works...
# Don't ask me why...
my $checksum_str = sprintf("%6o", $checksum);
$tar =~ s/^(.{148}).{7}/$1.$checksum_str."\x00"/e; # Overwrite checksum bytes
$tar .= "\x00" x 12; # create a block of 512, the header is 500 bytes.
$full_tar .= $tar;
}
method add_file($filepath, $data, $mode, $uid, $gid) {
$filepath = '.' . $filepath if $filepath =~ /^\//; # When given an absolute path, we actually need to make it ./
die "Path is longer than 98 chars" if length($filepath) > 98;
die "Mode is too long" if length(sprintf("%07o", int($mode))) > 7;
die "Uid is too long" if length(sprintf("%07o", int($uid))) > 7;
die "Gid is too long" if length(sprintf("%07o", int($gid))) > 7;
my $tar = $filepath . "\x00" x (100-length($filepath)); # char name[100]; /* 0 */
$tar .= sprintf("%07o", int($mode)) . "\x00"; # char mode[8]; /* 100 */
$tar .= sprintf("%07o", int($uid)) . "\x00"; # char uid[8]; /* 108 */
$tar .= sprintf("%07o", int($gid)) . "\x00"; # char gid[8]; /* 116 */
$tar .= sprintf("%011o", length($data)) . "\x00"; # char size[12]; /* 124 */
$tar .= sprintf("%011o", 566833020) . "\x00"; # char mtime[12]; /* 136 */
$tar .= "\x20" x 8; # char chksum[8]; /* 148 */ -> we'll do this later
$tar .= "0"; # char typeflag; /* 156 */ --> A regular file is 0
$tar .= "\x00" x 100; # char linkname[100]; /* 157 */
$tar .= "ustar\x00"; # char magic[6]; /* 257 */
$tar .= "00"; # char version[2]; /* 263 */
$tar .= "\x00" x 32; # char uname[32]; /* 265 */
$tar .= "\x00" x 32; # char gname[32]; /* 297 */
$tar .= "\x00" x 8; # char devmajor[8]; /* 329 */
lib/Container/Builder/Tar.pm view on Meta::CPAN
=over 1
=item new()
Create an empty C<Container::Builder::Tar> object.
=item get_tar()
Return a valid TAR file as a scalar string that is the concatenation of all the directories and files.
=item add_dir($path, $mode, $uid, $gid)
Add a directory to the TAR archive. Absolute directories will be prefixed with a '.'.
=item add_file($filepath, $data, $mode, $uid, $gid)
Add a file to the TAR archive. Absolute paths will be prefixed with a '.'.
=item extract_file($tar, $filepath)
Pass a valid TAR file as a scalar string and a filepath, and this method will return the header + data blocks of the file (or only the header when it's a directory).
=item extract_wildcard_files($tar, $filepath)
Does the same as C<extract_file()> but allows appending a wildcard (an asterisk) to the path to get all the files matching in the directory. Note that this does not recursively find all files, it will only match the files in the directory of the wild...
t/05_builder.t view on Meta::CPAN
# dirs
$b->create_directory('/', 0755, 0, 0);
$b->create_directory('/home', 0750, 0, 0);
$b->create_directory('/home/larry', 0750, 1337, 1337);
my @dirs = $b->get_dirs();
ok(@dirs == 3, '3 dirs will be created');
ok($dirs[0]->{path} eq '/', 'path is /');
ok($dirs[2]->{mode} == 0750, 'mode is 0750');
ok($dirs[2]->{uid} == 1337, 'uid is 1337');
ok($dirs[0]->{gid} == 0, 'gid is 0');
# users
$b->add_user('adri', 1000, 1000, '/bin/bash', '/home/adri');
$b->add_user('larry', 1337, 1337, '/bin/zsh', '/home/larry');
$b->add_user('bassie', 2000, 3000, '/bin/sh', '/home/bas');
my @users = $b->get_users();
ok(@users == 3, '3 users');
ok($users[0]->{name} eq 'adri');
ok($users[1]->{uid} == 1337);
ok($users[2]->{gid} == 3000);
ok($users[1]->{shell} eq '/bin/zsh');
ok($users[0]->{homedir} eq '/home/adri');
# groups
$b->add_group('users', 1000);
$b->add_group('leet', 1337);
$b->add_group('awesomepeople', 3000);
my @groups = $b->get_groups();
ok(@groups == 3, '3 groups');
ok($groups[0]->{name} eq 'users');
ok($groups[1]->{gid} == 1337);
ok($groups[2]->{name} eq 'awesomepeople');
done_testing;
( run in 2.104 seconds using v1.01-cache-2.11-cpan-5735350b133 )