Skip to content

Instantly share code, notes, and snippets.

@byrnedo
Last active March 7, 2018 07:56
Show Gist options
  • Save byrnedo/7ac5e461ba0866299fdd94899bebdbd9 to your computer and use it in GitHub Desktop.
Save byrnedo/7ac5e461ba0866299fdd94899bebdbd9 to your computer and use it in GitHub Desktop.
Stand in to merge docker compose file merging (docker stack deploy doesn't do the magic yet), does the 'extends' replacement and the env var replacement.
#!/usr/bin/perl
#
#====================================================
#
# Stand in for docker-compose 'extends' and env replacement
# ( currently not implemented for `docker stack deploy -f` )
#
#====================================================
use strict;
use warnings;
use YAML::Syck qw(LoadFile Dump);
use File::Spec;
use File::Basename;
use Hash::Merge qw(merge);
use Data::Dumper;
use Cwd qw(abs_path);
$YAML::Syck::SingleQuote = 1;
Hash::Merge::specify_behavior(
{
'SCALAR' => {
'SCALAR' => sub { $_[0] },
'ARRAY' => sub { $_[0] },
'HASH' => sub { $_[0] },
},
'ARRAY' => {
'SCALAR' => sub { [ @{ $_[0] }, $_[1] ] },
'ARRAY' => sub { [ @{ $_[1] }, @{ $_[0] } ] },
'HASH' => sub { [ @{ $_[0] }, values %{ $_[1] } ] },
},
'HASH' => {
'SCALAR' => sub { $_[0] },
'ARRAY' => sub { $_[0] },
'HASH' => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) },
},
},
'COMPOSE'
);
Hash::Merge::set_behavior('COMPOSE');
my @confFileList = map{ [$_, LoadFile($_)] }@ARGV;
sub parseExtends{
my $service = $_[0];
my $serviceMap = $_[1];
my $dir = $_[2];
my $extends = $service->{extends};
if ( exists($extends->{service}))
{
my $serviceToMerge = $extends->{service};
my $extendedServiceDef;
if (exists($extends->{'file'})) {
$extendedServiceDef = getExtendedService($extends->{file}, $extends->{service}, $dir);
} else {
$extendedServiceDef = $serviceMap->{$extends->{service}};
}
delete $service->{extends};
my $newHash = merge( $service, $extendedServiceDef);
$service = $newHash;
}
return $service;
}
#====================================================
#
# Get the hash map for the service def in anotherfile
#
# TODO - recursive call if this extends
#====================================================
sub getExtendedService{
my $file = $_[0];
my $serviceToMerge = $_[1];
my $basePath = $_[2];
my $resolvedFile = $file;#File::Spec->catfile($basePath, $file);
my $loaded = LoadFile($resolvedFile);
my $serviceMap = $loaded->{services};
if (exists $serviceMap->{$serviceToMerge}->{extends}) {
$serviceMap->{$serviceToMerge} = parseExtends($serviceMap->{$serviceToMerge}, $serviceMap, dirname($resolvedFile));
}
return formatPathArgs($serviceMap->{$serviceToMerge}, dirname($file));
}
sub stripDotDot {
my @arr;
my $c = 0;
for (File::Spec->splitdir($_[0]))
{
if ($_ ne ".." && $c ne 0) {
push @arr, $_
}
else {
pop @arr
}
$c ++;
}
return File::Spec->catfile(@arr);
}
sub formatPathArgs {
my $service = $_[0];
return $service;
#my $dir =$_[1];
##========================
## Loop through and replace list of
## vars that are dir paths on host
## =======================
#for my $varName ('env_file') {
#if (exists $service->{$varName} ) {
#if(ref($service->{$varName}) eq 'ARRAY') {
#my @envFileList = $service->{$varName};
#for my $i (0 .. $#{ $service->{$varName} }){
#my $f = $service->{$varName}->[$i];
#my $p = $dir . '/' . $f;
#$service->{$varName}->[$i] = $p;
#}
#} else {
#my $p = $dir . '/' . $service->{$varName};
#$service->{$varName} = $p;
#}
#}
#}
#return $service;
}
sub replaceEnvs {
my $data = $_[0];
#TODO add in fallback parsing ${SOME_ENV:-fallback_value}
$data =~ s{(\$)?\$+(\w+)}{(defined $1) ? "$1$2" : (exists $ENV{$2}) ? $ENV{$2} : q//}ge;
$data =~ s{(\$)?\$+\{(\w+)\}}{(defined $1) ? "$1$2" : (exists $ENV{$2}) ? $ENV{$2} : q//}ge;
return $data;
}
for my $fileHashPair (@confFileList) {
my $fileName = @$fileHashPair[0];
my($vol,$dir,$file) = File::Spec->splitpath($fileName);
my $confMapRef = @$fileHashPair[1];
my $services = $confMapRef->{services};
for my $k (keys %$services)
{
my $service =$services->{$k};
if (exists $service->{extends}) {
$confMapRef->{services}->{$k} = parseExtends($service, $confMapRef->{services}, $dir);
}
$confMapRef->{services}->{$k} = formatPathArgs($confMapRef->{services}->{$k}, dirname($fileName));
}
}
my $totalConfMapRef = merge(reverse map{ @$_[1] }@confFileList);
my $newYaml = Dump($totalConfMapRef);
print "version: '3.4'\n";
my $c = 0;
for (split /^/, $newYaml) {
if ($_ !~ /^(")?version(")?:.*/ && $c != 0) {
#$_ = replaceEnvs($_);
print $_;
}
$c ++;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment