#
# (c) Jan Gehring <jan.gehring@gmail.com>
#
# vim: set ts=2 sw=2 tw=0:
# vim: set expandtab:
package Rex::Repositorio::Server::Docker::Repository;
use Mojo::Base 'Mojolicious::Controller';
use Data::Dumper;
use File::Spec;
use File::Path 'make_path', 'remove_tree';
use File::Basename qw'dirname';
use JSON::XS;
use MIME::Base64;
require IO::All;
our $VERSION = '1.2.1'; # VERSION
sub get_repo_images {
my ($self) = @_;
my $requested_file = $self->req->url;
$self->app->log->debug("Requested-File: $requested_file");
my $orig_url = $self->repo->{url};
$orig_url =~ s/\/$//;
$self->app->log->debug("Upstream-URL: $orig_url");
$requested_file =~ s/^\///;
my $orig_file_url = $orig_url . "/" . $requested_file;
$self->app->log->debug("Orig-File-URL: $orig_file_url");
my $repo_name = $self->param("repo_namespace") . "/" . $self->param("name");
my $repo_dir =
File::Spec->catdir( $self->app->get_repo_dir( repo => $self->repo->{name} ),
"repository", $repo_name );
my $repo_file = File::Spec->catfile( $repo_dir, "repo.json" );
if ( -f $repo_file ) {
my $content = IO::All->new($repo_file)->slurp;
$self->res->headers->add( 'Content-Type', 'application/json' );
$self->render( text => $content );
}
else {
my $base64_auth_string = MIME::Base64::encode_base64(
$self->repo->{upstream_user} . ":" . $self->repo->{upstream_password} );
$self->proxy_to(
$orig_file_url,
sub {
my ( $c, $tx ) = @_;
$c->app->log->debug("Got data from upstream...");
make_path( dirname($repo_file) );
open my $fh, '>', $repo_file or die($!);
binmode $fh;
print $fh $tx->res->body;
close $fh;
my $ref = $tx->res->json;
my $repo_dir = $self->app->get_repo_dir( repo => $self->repo->{name} );
for my $image_data ( @{$ref} ) {
my $image_dir =
File::Spec->catdir( $repo_dir, "images", $image_data->{id} );
make_path($image_dir);
open my $image_ep, '>',
File::Spec->catfile( $image_dir, 'endpoint.data' )
or die($!);
print $image_ep $tx->res->headers->header('R-Docker-Endpoints');
close $image_ep;
open my $image_lib, '>',
File::Spec->catfile( $image_dir, 'library.data' )
or die($!);
print $image_lib $orig_file_url;
close $image_lib;
}
open my $ep, '>', "$repo_file.endpoint" or die($!);
print $ep $tx->res->headers->header('R-Docker-Endpoints');
close $ep;
},
sub {
my ( $c, $tx ) = @_;
my $docker_token = $tx->res->headers->header('X-Docker-Token') || "";
$c->app->log->debug("Got my docker token: $docker_token");
$self->stash( 'upstream_docker_token', $docker_token );
$self->_fix_docker_headers($tx);
},
{ # some custom headers to get the docker token, so that we can
# authenticate against the image servers.
'X-Docker-Token' => 'true',
'Authorization' => "Basic $base64_auth_string",
},
);
}
}
sub put_repo {
my ($self) = @_;
my $repo_name = $self->param("repo_namespace") . "/" . $self->param("name");
my $repo_dir =
File::Spec->catdir( $self->app->get_repo_dir( repo => $self->repo->{name} ),
"repository", $repo_name );
my $ref = decode_json( $self->req->body );
my $store = [ map { { id => $_->{id} } } @{$ref} ];
make_path($repo_dir);
open( my $fh, ">", File::Spec->catfile( $repo_dir, "repo.json" ) ) or die($!);
print $fh encode_json($store);
close($fh);
my $image_dir =
File::Spec->catdir( $self->app->get_repo_dir( repo => $self->repo->{name} ),
"images", $store->[-1]->{id} );
# make_path $image_dir;
# my $ancestor_file = File::Spec->catfile( $image_dir, "ancestors" );
#
# open( my $afh, ">", $ancestor_file ) or die($!);
# print $afh encode_json( [ map { $_ = $_->{id} } reverse @{$store} ] );
# close($afh);
#$self->render( json => {}, status => 500 );
#$self->render( json => {} );
$self->render( text => '""' );
}
sub get_repo_tag {
my ($self) = @_;
my $requested_file = $self->req->url;
$self->app->log->debug("Requested-File: $requested_file");
my $orig_url = $self->repo->{url};
$orig_url =~ s/\/$//;
$self->app->log->debug("Upstream-URL: $orig_url");
$requested_file =~ s/^\///;
my $orig_file_url = $orig_url . "/" . $requested_file;
$self->app->log->debug("Orig-File-URL: $orig_file_url");
my $repo_name = $self->param("repo_namespace") . "/" . $self->param("name");
my $tag_dir =
File::Spec->catdir( $self->app->get_repo_dir( repo => $self->repo->{name} ),
"repository", $repo_name, "tags" );
my $ret = {};
if ( -d $tag_dir ) {
opendir( my $dh, $tag_dir );
while ( my $entry = readdir($dh) ) {
next if ( $entry =~ m/^\./ );
next if ( !-f File::Spec->catfile( $tag_dir, $entry ) );
$ret->{$entry} =
IO::All->new( File::Spec->catfile( $tag_dir, $entry ) )->slurp;
}
closedir($dh);
$self->render( json => $ret );
}
else {
$self->proxy_to(
$orig_file_url,
sub {
my ( $c, $tx ) = @_;
$c->app->log->debug("Got data from upstream...");
make_path($tag_dir);
my $ref = $tx->res->json;
for my $e ( @{$ref} ) {
open my $fh, ">", File::Spec->catfile( $tag_dir, $e->{name} )
or die($!);
print $fh $self->_get_image_directory( $e->{layer} );
close $fh;
}
},
sub {
# TODO: check why we need to rewrite content
# must rewrite content... don't know why...
my ( $c, $tx ) = @_;
my $content = Mojo::Content::Single->new;
$content->headers( $tx->res->headers() );
my $ref = $tx->res->json;
my $new_ref = {};
for my $e ( @{$ref} ) {
$new_ref->{ $e->{name} } = $self->_get_image_directory( $e->{layer} );
}
my $new_content = Mojo::JSON::encode_json($new_ref);
my $asset = Mojo::Asset::Memory->new;
$asset->add_chunk($new_content);
$content->asset($asset);
$content->headers->content_length( $asset->size );
$tx->res->content($content);
},
sub {
my ( $c, $tx ) = @_;
$self->_fix_docker_headers($tx);
}
);
}
}
sub put_repo_tag {
my ($self) = @_;
my $tag_sha = $self->req->body;
$tag_sha =~ s/"//g;
my $repo_name = $self->param("repo_namespace") . "/" . $self->param("name");
my $url = $self->req->url;
my ($tag_name) = ( $url =~ m/.*\/(.+?)$/ );
my $tag_dir =
File::Spec->catdir( $self->app->get_repo_dir( repo => $self->repo->{name} ),
"repository", $repo_name, "tags" );
make_path($tag_dir);
open( my $fh, ">", File::Spec->catfile( $tag_dir, $tag_name ) ) or die($!);
print $fh $tag_sha;
close($fh);
$self->render( text => 'true' );
}
sub put_repo_image {
my ($self) = @_;
$self->render( text => '', status => 204 );
}
sub _get_image_directory {
my ( $self, $id ) = @_;
my $repo_dir = $self->app->get_repo_dir( repo => $self->repo->{name} );
my $images_dir = File::Spec->catdir( $repo_dir, "images" );
if ( -d File::Spec->catdir( $images_dir, $id ) ) {
return File::Spec->catdir( $images_dir, $id );
}
# try to guess the image directory...
# TODO: look inside repo.json for better preci... genauigkeit
opendir( my $dh, $images_dir ) or die($!);
while ( my $entry = readdir($dh) ) {
if ( $entry =~ m/^\Q$id\E/ ) {
return $entry;
}
}
closedir($dh);
}
sub _fix_docker_headers {
my ( $self, $tx ) = @_;
my $docker_endpoint = $tx->res->headers->header('X-Docker-Endpoints');
$tx->res->headers->remove('X-Docker-Endpoints');
$tx->res->headers->remove('X-Docker-Token');
$self->app->log->debug("Setting R-Docker-Endpoints to -> $docker_endpoint");
$tx->res->headers->add( 'R-Docker-Endpoints', $docker_endpoint )
if $docker_endpoint;
if ( $self->res->headers->header('WWW-Authenticate') ) {
$tx->res->headers->add( 'WWW-Authenticate',
$self->res->headers->header('WWW-Authenticate') );
}
if ( $self->res->headers->header('X-Docker-Token') ) {
$tx->res->headers->add( 'X-Docker-Token',
$self->res->headers->header('X-Docker-Token') );
}
$tx->res->headers->add( 'X-Docker-Endpoints' => $self->req->headers->host );
$tx->res->headers->add( 'Pragma' => 'no-cache' );
$tx->res->headers->add( 'Expires' => '-1' );
}
1;