#!raku
use Sparky::JobApi;
class Pipeline
does Sparky::JobApi::Role
{
method stage-main {
directory "scm";
my $test_env = tags()<test_env> || "qemu";
my $version = tags()<version> || "10";
my $ssh_user = ( $test_env eq "orb" ) ?? %*ENV<USER> !! "admin";
my $ssh_port = ( $test_env eq "orb" ) ?? 22 !! tags()<ssh_port> || 10022;
my $host = ( $test_env eq "orb" ) ?? 'rocky@orb' !! "127.0.0.1";
my $api = ( $test_env eq "orb" ) ?? "http://host.orb.internal:4000" !! "http://10.0.2.2:4000";
my $bootstrap-mode = tags()<bootstrap> ?? True !! False;
my $delegate = tags()<delegate> || "";
my $delegated = tags()<delegated> || False;
my $skip-test = tags()<skip_test> || False;
my $current-version = "";
my $distro;
my $prefix = tags()<prefix> || "default";
my $base-dir = "{%*ENV<HOME>}/rocky-linux-distro/$prefix";
my $job-desc;
my $ssh_key_path = tags()<ssh_key_path> ??
tags()<ssh_key_path>.subst(/^^ \s* "~/" /,%*ENV<HOME> ~ "/") !!
%*ENV<HOME> ~ '/.ssh/id_rsa.pub';
my $ssh_private_key = $ssh_key_path.subst(/".pub" $$/,"");
if $test_env eq "qemu" {
if "{$base-dir}/.version".IO ~~ :f {
$current-version = "{$base-dir}/.version".IO.slurp();
}
$distro = config()<distros><qemu>{"version{$version}"};
if $current-version.chomp ne $version {
say "current version ($current-version) ne required version ($version), forcing bootstrap";
$bootstrap-mode = True;
}
}
my $kickstart-mode = ( $test_env eq "qemu_with_kickstart" && ! tags()<kickstart_file> ) ?? True !! False;
my $ks-file = tags()<kickstart_file> || "";
my $kickstart-filter = tags()<kickstart_filter> || "";
say "=====";
say "base-dir: $base-dir";
say "test_env: $test_env";
say "version: $version" if $test_env eq "qemu";
say "current-version: $current-version" if $test_env eq "qemu";
say "distro: $distro" if $test_env eq "qemu";
say "testbox: $host";
say "ssh_user: $ssh_user";
say "ssh_key_path: $ssh_key_path";
say "ssh_private_key: $ssh_private_key";
say "prefix: $prefix";
say "ssh_port: $ssh_port";
say "api: $api";
say "delegate: $delegate";
say "delegated: $delegated";
say "skip_test: $skip-test";
say "qemu_shut: {tags()<qemu_shut> || False}";
say "bootstrap-mode: $bootstrap-mode" unless $test_env eq "qemu_with_kickstart";
say "kickstart-mode: $kickstart-mode" if $test_env eq "qemu_with_kickstart";
say "kickstart_file: $ks-file" if $test_env eq "qemu_with_kickstart";
say "=====";
my $repo-dir;
if tags()<delegated> {
my $me = Sparky::JobApi.new: :mine;
my $blob = $me.get-file("tasks.tar");
directory "tasks";
my $fh = open "tasks/tasks.tar", :w, :bin;
$fh.write($blob);
$fh.close;
bash "tar -xf tasks.tar && ls -l", %(
cwd => "{$*CWD}/tasks",
description => "unpack tasks.tar",
);
$job-desc = tags()<name>;
} else {
my $use_case_repo = tags()<use_case_repo>.subst(/^^ \s* "~/" /,%*ENV<HOME> ~ "/");
if $ks-file {
my $me = Sparky::JobApi.new: :mine;
my $blob = $me.get-file("tasks.tar");
directory "tasks";
my $fh = open "tasks/tasks.tar", :w, :bin;
$fh.write($blob);
$fh.close;
bash "tar -xf tasks.tar && ls -l", %(
cwd => "{$*CWD}/tasks",
description => "unpack tasks.tar",
);
}
file-delete "tasks.tar";
say "archiving tasks/ to tasks.tar";
bash "tar cf {$*CWD}/tasks.tar -C tasks/ .";
directory "use_case_repo";
file-delete "use_case_repo.tar";
if $use_case_repo.IO ~~ :d {
$repo-dir = $use_case_repo;
say "archiving {$use_case_repo} files to use_case_repo.tar";
bash "tar cf {$*CWD}/use_case_repo.tar -C {$use_case_repo} .";
$job-desc = "local: $use_case_repo";
} else {
$repo-dir = "use_case_repo";
git-scm tags()<use_case_repo>, %(
to => "use_case_repo",
branch => "HEAD",
);
say "(git) archiving {tags()<use_case_repo>} files to use_case_repo.tar";
bash "git archive --format=tar -o {$*CWD}/use_case_repo.tar HEAD", %(
cwd => "{$*CWD}/use_case_repo",
);
$job-desc = "git: {tags()<use_case_repo>}";
}
if $ks-file {
$job-desc = "{$job-desc} kickstart, file={$ks-file}";
say "use kickstart file: {$repo-dir}/{$ks-file}, set bootstrap_mode to true";
say "distro set from boot_iso: {tags()<boot_iso>}";
say "kickstart_url: {tags()<kickstart_url>}";
$bootstrap-mode = True;
my $me = Sparky::JobApi.new: :mine;
my $c = "{$repo-dir}/{$ks-file}".IO.slurp;
$c = "$c\n%post\ndnf install cloud-init tar -y\nsystemctl enable cloud-init.service\nsystemctl start cloud-init.service\n%end\n";
$c.= subst(/^^ url .*? $$/,"");
$c = "$c\n{tags()<kickstart_url>}";
"ks.tmp".IO.spurt($c);
$me.put-file("ks.tmp","ks.cfg");
$distro = tags()<boot_iso>;
}
if tags()<delegate> {
my $me = Sparky::JobApi.new: :mine;
my $project = $me.info.<project>;
my $job-id = $me.info.<job-id>;
my $rj = Sparky::JobApi.new(:$project,:$job-id, :api(tags()<delegate>));
$rj.put-file("{$*CWD}/use_case_repo.tar","use_case_repo.tar");
$rj.put-file("{$*CWD}/tasks.tar","tasks.tar");
my $tags = %( :delegated );
for tags().kv -> $k, $v {
next if $k ~~ /^^ SPARKY_/;
next if $k eq "delegate";
$tags{$k} = $v;
}
$rj.queue({
description => tags()<name>,
:$tags,
}),
return;
} else {
my $me = Sparky::JobApi.new: :mine;
$me.put-file("{$*CWD}/use_case_repo.tar","use_case_repo.tar");
$me.put-file("{$*CWD}/tasks.tar","tasks.tar");
if $kickstart-mode {
my $i = 0;
for dir($repo-dir, test => /'.ks' $$/) -> $f {
if $kickstart-filter {
next unless $f.basename eq $kickstart-filter
}
$i++;
my $project = $me.info.<project>;
my $job-id = "{$me.info.<job-id>}_ks_{$i}";
my $ks = Sparky::JobApi.new(:$project,:$job-id);
$ks.put-file("{$*CWD}/use_case_repo.tar","use_case_repo.tar");
$ks.put-file("{$*CWD}/tasks.tar","tasks.tar");
my $tags = %( kickstart_file => $f.basename );
for tags().kv -> $k, $v {
next if $k ~~ /^^ SPARKY_/;
$tags{$k} = $v;
}
$ks.queue({
description => "$job-desc kickstart, file={$f.basename}",
:$tags,
}),
}
return
}
}
}
directory $base-dir;
if $bootstrap-mode and $test_env ne "orb" {
say "bootstrap qemu VM";
say "====";
task-run "tasks/stop-qemu-box", %(
prefix => $prefix,
);
# spawns a child job
my $project = "qemu-session.$prefix";
my $j = self.new-job: :$project;
my $tags = {
stage => "qemu-session",
version => $version,
distro_url => $distro,
qemu_binary => tags()<qemu_binary>,
ssh_key_path => $ssh_key_path,
qemu_machine => tags()<qemu_machine>,
parent_job_id => tags()<SPARKY_JOB_ID>,
parent_job_name => tags()<SPARKY_PROJECT>,
api => $api,
prefix => $prefix,
ssh_port => $ssh_port,
ks_file => $ks-file,
};
$tags<use-kick-start> = True if $ks-file;
$j.queue({
description => $job-desc,
tags => $tags,
});
unless $ks-file {
say "save current version to {$base-dir}/.version";
"{$base-dir}/.version".IO.spurt($version);
}
say "queue qemu-session job, ",$j.info.raku;
say "wait till qemu box is ready, timeout 12 minutes";
my $ssh-ok = False;
for 1 .. 120 -> $i {
my $s = task-run "tasks/check-ssh", %(
:$host,
:$ssh_port,
:$ssh_user,
:$ssh_private_key,
);
if $s<state> eq "alive" {
$ssh-ok = True;
last;
}
sleep(10);
say "[$i] ...";
say "qemu-session job status: ", $j.status;
if $j.status eq "FAIL" {
say "qemu-session job failed, stopping qemu vm and exiting ...";
task-run "tasks/stop-qemu-box", %(
prefix => $prefix,
);
exit(1);
}
}
if $ssh-ok == False {
say "qemu-session job timeouted - exiting ...";
if tags()<qemu_shut> {
task-run "tasks/stop-qemu-box", %(
prefix => $prefix,
);
}
exit(1);
}
} elsif $bootstrap-mode and $test_env eq "orb" {
say "bootstrap orb VM";
say "====";
bash "orb delete rocky -f||:";
bash "orb create rocky";
bash "orb start rocky";
}
if $skip-test {
say "skip_test is enabled, exiting ...";
return;
}
say "run use case scenario on vm box";
my $project = "test-use-case.$prefix";
my $j = Sparky::JobApi.new: :$project;
my $bootstrap = $bootstrap-mode;
$j.queue({
description => $job-desc,
tags => %(
stage => "use-case",
dump_task_code => tags()<dump_task_code> ?? "on" !! "off",
parent_job_id => tags()<SPARKY_JOB_ID>,
parent_job_name => tags()<SPARKY_PROJECT>,
api => $api,
ks_file => $ks-file,
),
sparrowdo => %(
:$ssh_user,
:$ssh_port,
:$ssh_private_key,
:$host,
:no_sudo,
:$bootstrap,
#:debug,
),
});
say "queue use case job, ",$j.info.raku;
say "wait up to 15 minutes till use case scenario has finished";
my $st = self.wait-job($j, %( :900timeout ) );
#say $j.report();
# check if child job send us any message
my @meta = $j.meta;
say @meta.raku;
if @meta && @meta[0]<reboot> eq "need" {
say "reboot signal recieved from child job, reboot qemu box";
$project = "vm-reboot.$prefix";
my $j = Sparky::JobApi.new: :$project;
$j.queue({
description => $job-desc,
tags => %(
:stage<vm-reboot>,
:$test_env,
:$api,
),
sparrowdo => %(
:$ssh_user,
:$ssh_port,
:$host,
:no_sudo,
#:debug,
),
});
say "queue vm-reboot job, ",$j.info.raku;
say "wait up to 7 minutes till reboot has finished";
my $st = self.wait-job($j, %( :420timeout ) );
unless $st<OK> {
if $test_env ne "orb" {
say "qemu box is not good, stopping qemu session";
task-run "tasks/stop-qemu-box", %(
prefix => $prefix,
);
}
say "vm box is not good, exiting ...";
exit(1);
}
sleep(15); # wait till VM has rebooted
my $ssh-ok = False;
for 1 .. 40 -> $i {
my $s = task-run "tasks/check-ssh", %(
:$ssh_user,
:$ssh_port,
:$host,
:$ssh_private_key,
);
if $s<state> eq "alive" {
$ssh-ok = True;
last;
}
sleep(10);
say "[$i] ...";
}
if $ssh-ok == True {
say "machine successfully rebooted, run use case scenario on qemu box";
$project = "test-use-case.$prefix";
my $j = Sparky::JobApi.new: :$project;
$j.queue({
description => $job-desc,
tags => %(
stage => "use-case",
dump_task_code => tags()<dump_task_code> ?? "on" !! "off",
parent_job_id => tags()<SPARKY_JOB_ID>,
parent_job_name => tags()<SPARKY_PROJECT>,
api => $api,
ks_file => $ks-file,
:reboot-ok,
),
sparrowdo => %(
:$ssh_user,
:$ssh_port,
:$host,
:no_sudo,
:$ssh_private_key,
#:debug,
),
});
say "queue use case job, ",$j.info.raku;
say "wait up to 15 minutes till use case scenario has finished";
my $st = self.wait-job($j, %( :900timeout ) );
if tags()<qemu_shut> {
task-run "tasks/stop-qemu-box", %(
prefix => $prefix,
);
}
} else {
# handle case when vm has not showed up after reboot
say "vm box is not good, stopping qemu session";
if $test_env ne "orb" {
task-run "tasks/stop-qemu-box", %(
prefix => $prefix,
);
}
say "vm box is not good, exiting";
exit(1);
}
# reboot is not required, just stop VM in case qemu_shut is enabled
} elsif tags()<qemu_shut> {
task-run "tasks/stop-qemu-box", %(
prefix => $prefix,
);
}
}
method stage-qemu-session {
my $job = Sparky::JobApi.new(
project => tags()<parent_job_name>,
job-id => tags()<parent_job_id>,
);
my $blob = $job.get-file("tasks.tar");
directory "tasks";
my $fh = open "tasks/tasks.tar", :w, :bin;
$fh.write($blob);
$fh.close;
bash "tar -xf tasks.tar && ls -l", %(
cwd => "{$*CWD}/tasks",
description => "unpack tasks.tar",
);
if tags()<use-kick-start> {
my $ks_url = "http://10.0.2.2:4000/file_view/{tags()<parent_job_name>}/{tags()<parent_job_id>}/ks.cfg";
my $ks-version = tags()<distro_url>.subst("/","_",:g).
subst(":","_",:g).subst(".","_").tail.chomp;
task-run "tasks/kickstart-bootstrap", %(
distro_url => tags()<distro_url>,
ssh_key_path => tags()<ssh_key_path>,
prefix => tags()<prefix>,
machine => tags()<qemu_machine>,
qemu_binary => tags()<qemu_binary>,
version => $ks-version,
ks_url => $ks_url,
ssh_port => tags()<ssh_port>,
);
}
unless tags()<use-kick-start> {
task-run "tasks/setup-qemu-image", %(
distro_url => tags()<distro_url>,
ssh_key_path => tags()<ssh_key_path>,
prefix => tags()<prefix>,
version => tags()<version>,
);
}
task-run "tasks/run-qemu-box", %(
iso => "{%*ENV<HOME>}/rocky-linux-distro/{tags()<prefix>}/distro.qcow2",
seed => "/tmp/init.iso",
qemu_binary => tags()<qemu_binary>,
machine => tags()<qemu_machine>,
prefix => tags()<prefix>,
ssh_port => tags()<ssh_port>,
);
}
method stage-use-case {
directory "scm";
bash "echo OK";
my $job = Sparky::JobApi.new(
api => tags()<api>,
project => tags()<parent_job_name>,
job-id => tags()<parent_job_id>,
);
my $blob = $job.get-file("use_case_repo.tar");
my $fh = open "scm/use_case_repo.tar", :w, :bin;
$fh.write($blob);
$fh.close;
bash "tar -xf use_case_repo.tar && ls -l", %(
cwd => "{$*CWD}/scm",
description => "unpack use_case_repo.tar",
);
if "scm/main.raku".IO ~~ :e {
say "load scenario from main.raku";
chdir "scm/";
if tags()<dump_task_code> eq "on" {
say "enable dump_task_code ...";
%*ENV<SP6_DUMP_TASK_CODE> = 1;
}
EVALFILE "main.raku";
} else {
warn "no file main.raku found, nothing to do"
}
}
method stage-vm-reboot {
say "rebooting machine ...";
task-run "reboot", "reboot";
}
}
Pipeline.new.run;