Sysync
view release on metacpan or search on metacpan
lib/Sysync.pm view on Meta::CPAN
my $keys = join("\n", @{$user->{ssh_keys} || []});
$keys .= "\n" if $keys;
next unless $keys;
push @ssh_keys, {
username => $user->{username},
keys => $keys,
uid => $user->{uid},
};
}
return {
passwd => $passwd,
shadow => $shadow,
group => $group,
gshadow => $gshadow,
ssh_keys => \@ssh_keys,
data => $data,
};
}
=head3 get_host_files
Generate a list of files with their content.
Returns hashref:
'/etc/filename.conf' => {
mode => 600,
gid => 0,
uid => 0,
data => 'data is here'
}
=cut
sub get_host_files { die 'needs implemented' }
=head3 update_host_files
Build host files from specifications.
=cut
sub update_host_files
{
my ($self, $host) = @_;
my $stagefilesdir = $self->stagefilesdir;
my $stagedir = $self->stagedir;
my $r = 0;
next unless $self->is_valid_host($host);
my $files = $self->get_host_files($host);
unless (-d "$stagefilesdir/$host")
{
mkdir "$stagefilesdir/$host";
chmod 0755, "$stagefilesdir/$host";
chown 0, 0, "$stagefilesdir/$host";
$self->log("creating: $stagefilesdir/$host");
$r++;
}
unless (-d "$stagefilesdir/$host/etc")
{
mkdir "$stagefilesdir/$host/etc";
chmod 0755, "$stagefilesdir/$host/etc";
chown 0, 0, "$stagefilesdir/$host/etc";
$self->log("creating: $stagefilesdir/$host/etc");
$r++;
}
unless (-d "$stagefilesdir/$host/etc/ssh")
{
mkdir "$stagefilesdir/$host/etc/ssh";
chmod 0755, "$stagefilesdir/$host/etc/ssh";
chown 0, 0, "$stagefilesdir/$host/etc/ssh";
$self->log("creating: $stagefilesdir/$host/etc/ssh");
$r++;
}
unless (-d "$stagefilesdir/$host/etc/ssh/authorized_keys")
{
mkdir "$stagefilesdir/$host/etc/ssh/authorized_keys";
chmod 0755, "$stagefilesdir/$host/etc/ssh/authorized_keys";
chown 0, 0, "$stagefilesdir/$host/etc/ssh/authorized_keys";
$self->log("creating: $stagefilesdir/$host/etc/ssh/authorized_keys");
$r++;
}
for my $path (sort keys %{ $files || {} })
{
my $item = $files->{$path};
next unless $item->{directory};
$item->{directory} =~ s/\/$//;
next if $item->{directory} eq '/etc';
next if $item->{directory} eq '/etc/ssh';
next if $item->{directory} eq '/etc/ssh/authorized_keys';
$item->{directory} =~ s/^\///;
my @path_parts = split('/', $item->{directory});
my $filename = pop @path_parts;
my $parent_dir = join('/', @path_parts);
$item->{file} =~ s/^\///;
unless (-d "$stagefilesdir/$host/$parent_dir")
{
die "[$host: error] parent directory $parent_dir not defined for $item->{directory}\n";
}
unless (-d "$stagefilesdir/$host/$item->{directory}")
{
mkdir "$stagefilesdir/$host/$item->{directory}";
$self->log("creating: $stagefilesdir/$host/$item->{directory}");
}
my $mode = sprintf("%04i", $item->{mode});
chmod $mode, "$stagefilesdir/$host/$item->{directory}";
chown $item->{uid}, $item->{gid}, "$stagefilesdir/$host/$item->{directory}";
$r++;
}
for my $path (keys %{ $files || {} })
{
my $item = $files->{$path};
next unless $item->{file};
my @path_parts = split('/', $item->{file});
my $filename = pop @path_parts;
my $parent_dir = join('/', @path_parts);
$item->{file} =~ s/^\///;
unless (-d "$stagefilesdir/$host/$parent_dir")
{
die "[$host: error] directory $parent_dir not defined for $item->{file}\n";
}
if ($self->write_file_contents("$stagefilesdir/$host/$item->{file}", $item->{data}))
{
$r++;
}
my $mode = sprintf("%04i", $item->{mode});
chmod oct($mode), "$stagefilesdir/$host/$item->{file}";
chown $item->{uid}, $item->{gid}, "$stagefilesdir/$host/$item->{file}";
}
# get list of staging directory contents
my @staged_file_list;
File::Find::find({
wanted => sub { push @staged_file_list, $_ },
no_chdir => 1,
}, "$stagefilesdir/$host");
for my $staged_file (@staged_file_list)
{
next unless -e $staged_file;
(my $local_staged_file = $staged_file) =~ s/^$stagefilesdir\/$host//;
next unless $local_staged_file;
next if $local_staged_file eq '/';
next if $local_staged_file eq '/etc';
next if $local_staged_file eq '/etc/ssh';
next if $local_staged_file eq '/etc/ssh/authorized_keys';
next if $local_staged_file eq '/var/lib/extrausers';
unless ($files->{$local_staged_file})
{
if (-d $staged_file)
{
$self->log("deleting directory: $staged_file");
rmtree($staged_file);
}
elsif (-e $staged_file)
{
$self->log("deleting file: $staged_file");
unlink($staged_file);
}
$r++;
}
}
return $r;
}
=head3 update_all_hosts
Iterate through every host and build password files.
=cut
sub update_all_hosts
{
my ($self, %params) = @_;
# get list of hosts along with image name
my $hosts = $params{hosts} || $self->get_all_hosts;
# first, build staging directories
my @hosts = keys %{ $hosts->{hosts} || {} };
my $stagedir = $self->stagedir;
my $r = 0;
if ($hosts->{extra_users} || $self->{extra_users})
{
$hosts->{passwd_file} = '/var/lib/extrausers/passwd';
$hosts->{group_file} = '/var/lib/extrausers/group';
$hosts->{shadow_file} = '/var/lib/extrausers/shadow';
$hosts->{gshadow_file} = '/var/lib/extrausers/gshadow';
}
my $passwd_file = $hosts->{passwd_file} || '/etc/passwd';
my $group_file = $hosts->{group_file} || '/etc/group';
my $shadow_file = $hosts->{shadow_file} || '/etc/shadow';
my $gshadow_file = $hosts->{gshadow_file} || '/etc/gshadow';
for my $host (@hosts)
{
next unless $self->is_valid_host($host);
unless (-d "$stagedir/$host")
{
mkdir "$stagedir/$host";
chmod 0755, "$stagedir/$host";
chown 0, 0, "$stagedir/$host";
$self->log("creating: $stagedir/$host");
$r++;
}
unless (-d "$stagedir/$host/etc")
{
mkdir "$stagedir/$host/etc";
chmod 0755, "$stagedir/$host/etc";
chown 0, 0, "$stagedir/$host/etc";
$self->log("creating: $stagedir/$host/etc");
$r++;
}
unless (-d "$stagedir/$host/etc/ssh")
{
mkdir "$stagedir/$host/etc/ssh";
chmod 0755, "$stagedir/$host/etc/ssh";
chown 0, 0, "$stagedir/$host/etc/ssh";
$self->log("creating: $stagedir/$host/etc/ssh");
$r++;
}
unless (-d "$stagedir/$host/etc/ssh/authorized_keys")
{
mkdir "$stagedir/$host/etc/ssh/authorized_keys";
chmod 0755, "$stagedir/$host/etc/ssh/authorized_keys";
chown 0, 0, "$stagedir/$host/etc/ssh/authorized_keys";
$self->log("creating: $stagedir/$host/etc/ssh/authorized_keys");
$r++;
}
unless (-d "$stagedir/$host/var")
{
mkdir "$stagedir/$host/var";
chmod 0755, "$stagedir/$host/var";
chown 0, 0, "$stagedir/$host/var";
$self->log("creating: $stagedir/$host/var");
$r++;
}
unless (-d "$stagedir/$host/var/lib")
{
mkdir "$stagedir/$host/var/lib";
chmod 0755, "$stagedir/$host/var/lib";
chown 0, 0, "$stagedir/$host/var/lib";
$self->log("creating: $stagedir/$host/var/lib");
$r++;
}
unless (-d "$stagedir/$host/var/lib/extrausers")
{
mkdir "$stagedir/$host/var/lib/extrausers";
chmod 0755, "$stagedir/$host/var/lib/extrausers";
chown 0, 0, "$stagedir/$host/var/lib/extrausers";
$self->log("creating: $stagedir/$host/var/lib/extrausers");
$r++;
}
# write host files
my $ent_data = $self->get_host_ent($host);
next unless $ent_data;
for my $key (@{ $ent_data->{ssh_keys} || [] })
{
my $username = $key->{username};
my $uid = $key->{uid};
my $text = $key->{keys};
if ($self->write_file_contents("$stagedir/$host/etc/ssh/authorized_keys/$username", $text))
{
chmod 0600, "$stagedir/$host/etc/ssh/authorized_keys/$username";
chown $uid, 0, "$stagedir/$host/etc/ssh/authorized_keys/$username";
$r++;
}
}
my ($shadow_group) =
grep { $_->{groupname} eq 'shadow' }
@{ $ent_data->{data}{groups} || [ ] };
$shadow_group = {} unless defined $shadow_group;
$shadow_group = $shadow_group->{gid} || 0;
if ($self->write_file_contents("$stagedir/$host/$passwd_file", $ent_data->{passwd}))
{
chmod 0644, "$stagedir/$host/$passwd_file";
chown 0, 0, "$stagedir/$host/$passwd_file";
$r++;
}
if ($self->write_file_contents("$stagedir/$host/${group_file}", $ent_data->{group}))
{
chmod 0644, "$stagedir/$host/${group_file}";
chown 0, 0, "$stagedir/$host/${group_file}";
$r++;
}
if ($self->write_file_contents("$stagedir/$host/${shadow_file}", $ent_data->{shadow}))
{
chmod 0640, "$stagedir/$host/${shadow_file}";
chown 0, $shadow_group, "$stagedir/$host/${shadow_file}";
$r++;
}
if ($self->write_file_contents("$stagedir/$host/${gshadow_file}", $ent_data->{gshadow}))
{
chmod 0640, "$stagedir/$host/${gshadow_file}";
chown 0, $shadow_group, "$stagedir/$host/${gshadow_file}";
$r++;
}
}
return $r;
}
=head3 write_file_contents
=cut
sub write_file_contents
{
my ($self, $file, $data) = @_;
# check to see if this differs
if (-e $file)
{
if (md5_hex($data) eq md5_hex($self->read_file_contents($file)))
{
return;
}
}
$self->log("writing: $file");
if (-e $file)
{
unlink($file) or die $!;
}
open(F, "> $file") or die $!;
print F $data;
close(F);
return 1;
}
=head3 read_file_contents
=cut
sub read_file_contents
{
my ($self, $file, %params) = @_;
die "error: $file does not exist\n"
if $params{must_exist} and not -f $file;
return unless -e $file;
open(my $fh, $file);
my @content = <$fh>;
close($fh);
return join('', @content);
}
1;
( run in 0.520 second using v1.01-cache-2.11-cpan-5511b514fd6 )