view release on metacpan or search on metacpan
lib/App/Ordo.pm view on Meta::CPAN
sub ensure_session {
my ($self) = @_;
my $config = $self->api->config;
my $token = $config->{token} // '';
my $res = $self->api->call('login_user', { token => $token });
if ($res->{success}) {
$CURRENT_PATH = $res->{path} // '/';
say colored(["bold green"], "Connected to " . $self->api->config->{api} . " as $res->{email}");
return $res;
}
say colored(["bold yellow"], "No valid token found");
return $self->prompt_for_token;
}
sub prompt_for_token {
my $self = shift;
while (1) {
print colored(["bold white"], "Enter API token: ");
chomp(my $token = <STDIN>);
$token =~ s/^\s+|\s+$//g;
my $res = $self->api->call('login_user', { token => $token });
if ($res->{success} && $res->{level} >= 1) {
my $config = $self->api->config;
$config->{token} = $token;
open my $fh, '>', $self->api->config_file or die "Cannot save config: $!";
print $fh encode_json($config);
close $fh;
$CURRENT_PATH = $res->{path} // '/';
say colored(["bold green"], "Logged in successfully");
return $res;
}
say colored(["bold red"], $res->{message} || "Invalid token");
}
}
# ------------------------------------------------------------------
# Interactive Shell
# ------------------------------------------------------------------
sub run_interactive {
my $self = shift;
$self->ensure_session;
my $term = Term::ReadLine::Perl5->new('ordo');
say colored(["bold white"], "\nWelcome to Ordo - the hierarchical job scheduler");
say "Type 'help' for commands, Ctrl-D to exit.\n";
while (defined(my $line = $term->readline("ordo:$CURRENT_PATH> "))) {
$line =~ s/^\s+|\s+$//g;
next unless $line;
my @args = extract_command($line);
App::Ordo::Runner->new(api => $self->api)->run(@args);
}
say colored(["bold yellow"], "\nGoodbye!");
}
# ------------------------------------------------------------------
# Command line parsing
# ------------------------------------------------------------------
sub extract_command {
my ($line) = @_;
return () unless defined $line;
my @args;
my $current = '';
lib/App/Ordo/Command/Base.pm view on Meta::CPAN
}
my %opt;
my $ok = GetOptionsFromArray(
\@args,
\%opt,
keys %{$self->option_spec},
);
unless ($ok) {
say colored(["bold red"], "Invalid options â use --help");
return;
}
$self->execute(\%opt, @args);
}
sub execute {
my ($self, $opt, @args) = @_;
die "execute() not implemented in " . ref($self);
}
sub show_help {
my ($self) = @_;
say colored(["bold cyan"], "ordo @{[lc $self->name]} " . $self->usage);
say $self->summary;
my $spec = $self->option_spec;
return unless $spec && keys %$spec;
say "\nOptions:";
for my $key (sort keys %$spec) {
my $desc = $spec->{$key} || '';
(my $clean = $key) =~ s/[=:!+].*//;
$clean =~ s/\|/ | /g;
lib/App/Ordo/Command/Cal/Attach.pm view on Meta::CPAN
sub name { "cal attach" }
sub summary { "Attach a calendar to a cluster" }
sub usage { "<calendar> <cluster-path>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $cal_name, $cluster_path) = @_;
unless ($cal_name && $cluster_path) {
say colored(["bold red"], "Usage: cal attach <calendar> <cluster-path>");
return;
}
my $res = $self->api->call('update_cluster', {
name => $cluster_path,
cal => $cal_name, # server accepts 'cal' => name
});
if ($res->{success}) {
say colored(["bold green"], "Calendar '$cal_name' attached to cluster '$cluster_path'");
} else {
say colored(["bold red"], "Failed: " . ($res->{message} || 'not found'));
}
}
1;
lib/App/Ordo/Command/Cal/Create.pm view on Meta::CPAN
return {
'description=s' => 'Description for the calendar',
# No positional options in spec â name is taken from @args
};
}
sub execute {
my ($self, $opt, $name) = @_;
unless ($name) {
say colored(["bold red"], "Error: calendar name is required");
$self->show_help;
return;
}
my $res = $self->api->call('create_cal', {
name => $name,
tz => $self->api->tz,
description => $opt->{description},
});
if ($res->{success}) {
say colored(["bold green"], "Calendar '$name' created successfully");
} else {
say colored(["bold red"], "Failed to create calendar: " . ($res->{message} || 'unknown error'));
}
}
1;
lib/App/Ordo/Command/Cal/Cron/Add.pm view on Meta::CPAN
sub option_spec {
return {
'description=s' => 'Description for this cron',
};
}
sub execute {
my ($self, $opt, $cal_name, $cron_expr) = @_;
unless ($cal_name && $cron_expr) {
say colored(["bold red"], "Usage: cal cron add <calendar> \"<cron>\" [--description \"...\"]");
return;
}
my $res = $self->api->call('create_cron', {
cal => $cal_name,
name => $cron_expr,
description => $opt->{description},
});
if ($res->{success}) {
say colored(["bold green"], "Cron '$cron_expr' added to calendar '$cal_name'");
} else {
say colored(["bold red"], "Failed: " . ($res->{message} || 'calendar not found'));
}
}
1;
lib/App/Ordo/Command/Cal/Cron/Delete.pm view on Meta::CPAN
sub aliases { ['cal cron rm'] }
sub option_spec {
return {};
}
sub execute {
my ( $self, $opt, $cron_id ) = @_;
unless ( $cron_id && $cron_id =~ /^\d+$/ ) {
say colored( ["bold red"], "Usage: cal cron delete <cron-id>" );
say "Example: cal cron delete 13";
return;
}
my $payload = {
# cal => $cal_name,
id => $cron_id,
};
$payload->{force} = 1 if $opt->{force};
my $res = $self->api->call( 'delete_cron', $payload );
if ( $res->{success} ) {
my $name = $res->{cal};
my $message = "Cron ID $cron_id removed from calendar";
$message .= " $res->{cal}" if $res->{cal};
say colored( ["bold green"], $message );
}
else {
say colored( ["bold red"], "Failed to delete cron: " . ( $res->{message} || 'unknown error' ) );
}
}
1;
lib/App/Ordo/Command/Cal/Delete.pm view on Meta::CPAN
sub option_spec {
return {
'force' => 'Force delete even if attached to clusters',
};
}
sub execute {
my ($self, $opt, $cal_name) = @_;
unless ($cal_name) {
say colored(["bold red"], "Usage: cal delete <calendar-name> [--force]");
say "Use --force if the calendar is attached to clusters";
return;
}
my $payload = { name => $cal_name };
$payload->{force} = 1 if $opt->{force};
my $res = $self->api->call('delete_cal', $payload);
if ($res->{success}) {
say colored(["bold green"], "Calendar '$cal_name' deleted");
say "All associated cron expressions removed (cascade delete)";
} else {
say colored(["bold red"], "Failed to delete calendar: " . ($res->{message} || 'unknown error'));
}
}
1;
lib/App/Ordo/Command/Cal/Detach.pm view on Meta::CPAN
sub name { "cal detach" }
sub summary { "Detach a calendar from a cluster" }
sub usage { "<calendar> <cluster-path>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $cal_name, $cluster_path) = @_;
unless ($cal_name && $cluster_path) {
say colored(["bold red"], "Usage: cal detach <calendar> <cluster-path>");
return;
}
my $res = $self->api->call('update_cluster', {
name => $cluster_path,
cal_id => '', # empty string = detach
});
if ($res->{success}) {
say colored(["bold green"], "Calendar '$cal_name' detached from cluster '$cluster_path'");
} else {
say colored(["bold red"], "Failed: " . ($res->{message} || 'not found'));
}
}
1;
lib/App/Ordo/Command/Cal/List.pm view on Meta::CPAN
sub usage { "[calendar-name]" }
sub aliases { ['cal ls'] }
sub option_spec { {} }
sub execute {
my ($self, $opt, $filter_name) = @_;
my $res = $self->api->call('find_cal', {});
unless ($res->{success} && scalar @{ $res->{cals} }) {
say colored(["bold yellow"], "No calendars found");
return;
}
my @cals = @{$res->{cals} || []};
# Optional: filter by name
if ($filter_name) {
@cals = grep { ($_->{name} || '') eq $filter_name } @cals;
unless (@cals) {
say colored(["bold yellow"], "No calendar found with name: $filter_name");
return;
}
}
say colored(["bold cyan"], "Calendars (" . scalar(@cals) . " found):\n");
for my $cal (@cals) {
my $name = $cal->{name} || colored(["yellow"], "(unnamed id:$cal->{id})");
my $desc = $cal->{description} ? " - $cal->{description}" : "";
my $attached = $cal->{cluster_ids} && @{$cal->{cluster_ids}}
? " (attached to " . scalar(@{$cal->{cluster_ids}}) . " clusters)"
: "";
say colored(["bold white"], " $name$desc$attached");
my $crons = $cal->{crons} || [];
if (@$crons) {
my $rows = [ [qw(ID CRON NEXT_START DESCRIPTION)] ];
for my $cron (@$crons) {
my $next = $cron->{next_start}
? strftime("%Y-%m-%d %H:%M", localtime($cron->{next_start}))
: '-';
my $desc = $cron->{description} || $cron->{english} || '';
push @$rows, [
lib/App/Ordo/Command/Cal/Show.pm view on Meta::CPAN
sub name { "cal show" }
sub summary { "Show detailed information about a calendar" }
sub usage { "<calendar-name>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $cal_name) = @_;
unless ($cal_name) {
say colored(["bold red"], "Usage: cal show <calendar-name>");
return;
}
my $res = $self->api->call('find_cal', { name => $cal_name });
unless ($res->{success}) {
say colored(["bold red"], "Calendar not found: $cal_name");
say " " . ($res->{message} || '');
return;
}
my ($cal) = grep { ($_->{name} || '') eq $cal_name } @{$res->{cals} || []};
unless ($cal) {
say colored(["bold red"], "Calendar '$cal_name' not found");
return;
}
my $name = $cal->{name} || colored(["yellow"], "(unnamed id:$cal->{id})");
my $tz = $cal->{tz} ? "$cal->{tz}" : "";
my $desc = $cal->{description} ? " - $cal->{description}" : "";
my $attached = $cal->{cluster_ids} && @{$cal->{cluster_ids}}
? join(", ", @{$cal->{cluster_ids}})
: "(not attached)";
say colored(["bold cyan"], "Calendar: $name$desc");
say colored(["bold cyan"], "TimeZone: $tz");
say colored(["bold cyan"], "Attached: $attached\n");
my $crons = $cal->{crons} || [];
if (@$crons) {
my $rows = [ [qw(ID CRON NEXT_START DESCRIPTION)] ];
for my $cron (@$crons) {
my $next = $cron->{next_start}
? strftime("%Y-%m-%d %H:%M", localtime($cron->{next_start}))
: '-';
my $desc = $cron->{description} || $cron->{english} || '';
push @$rows, [ $cron->{id}, $cron->{name}, $next, $desc ];
lib/App/Ordo/Command/Cd.pm view on Meta::CPAN
sub option_spec { {} }
sub execute {
my ($self, $opt, $target) = @_;
my $res = $self->api->call('change_cluster', { name => $target });
if ($res->{success}) {
$App::Ordo::CURRENT_PATH = $res->{path};
} else {
say colored(["bold red"], "Cannot cd to '$target': " . ($res->{message} || 'not found'));
}
}
1;
lib/App/Ordo/Command/Cluster/Complete.pm view on Meta::CPAN
sub name { "cluster complete" }
sub summary { "Force a cluster and all jobs to complete state" }
sub usage { "<path>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $path) = @_;
unless ($path) {
say colored(["bold red"], "Usage: cluster complete <path>");
return;
}
my $res = $self->api->call('complete_cluster', { name => $path });
if ($res->{success}) {
say colored(["bold green"], "Cluster '$path' marked complete - dependent clusters may now run");
} else {
say colored(["bold red"], "Failed: " . ($res->{message} || 'unknown error'));
}
}
1;
lib/App/Ordo/Command/Cluster/Create.pm view on Meta::CPAN
'loops=i' => 'Number of loops',
'clonable=i' => 'Allow cloning (0/1)',
'on_fail=s' => 'Cluster to run on failure',
};
}
sub execute {
my ($self, $opt, $path) = @_;
unless ($path) {
say colored(["bold red"], "Usage: cluster create <path> [options]");
return;
}
if ($opt->{needs}) {
my @expanded;
for my $item (@{$opt->{needs}}) {
push @expanded, split /,/, $item;
}
$opt->{needs} = \@expanded;
}
lib/App/Ordo/Command/Cluster/Create.pm view on Meta::CPAN
needs_any => $opt->{needs_any} || [],
cal => $opt->{cal},
loops => $opt->{loops},
clonable => $opt->{clonable},
on_fail => $opt->{on_fail},
};
my $res = $self->api->call('create_cluster', $payload);
if ($res->{success}) {
say colored(["bold green"], "Cluster '$path' created");
} else {
say colored(["bold red"], "Failed: " . ($res->{message} || 'unknown error'));
}
}
1;
lib/App/Ordo/Command/Cluster/Delete.pm view on Meta::CPAN
sub option_spec {
return {
'force|f' => 'Force delete even if jobs are running',
};
}
sub execute {
my ($self, $opt, $path) = @_;
unless ($path) {
say colored(["bold red"], "Usage: cluster delete <path> [--force]");
return;
}
#my $full_path = $path =~ m|^/| ? $path : "$CURRENT_PATH/$path";
#$full_path =~ s|//+|/|g;
my $payload = { name => $path };
$payload->{force} = 1 if $opt->{force};
my $res = $self->api->call('delete_cluster', $payload);
if ($res->{success}) {
say colored(["bold green"], "Cluster and all jobs deleted");
} else {
say colored(["bold red"], "Failed: " . ($res->{message} || 'unknown error'));
say "Hint: use --force if jobs are running" if $res->{message} && $res->{message} =~ /running|active/i;
}
}
1;
lib/App/Ordo/Command/Cluster/Hold.pm view on Meta::CPAN
sub name { "cluster hold" }
sub summary { "Prevent cluster from running" }
sub usage { "<path/name>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $name) = @_;
unless ($name) {
say colored(["bold red"], "Usage: cluster hold <path/name>");
return;
}
my $res = $self->api->call('hold_cluster', { name => $name });
$res->{success}
? say colored(["bold yellow"], "Cluster '$name' held")
: say colored(["bold red"], "Failed: " . ($res->{message} || 'error'));
}
1;
lib/App/Ordo/Command/Cluster/Ice.pm view on Meta::CPAN
sub name { "cluster ice" }
sub summary { "Skip cluster without blocking dependents" }
sub usage { "<path/name>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $name) = @_;
unless ($name) {
say colored(["bold red"], "Usage: cluster ice <path/name>");
return;
}
my $res = $self->api->call('ice_cluster', { name => $name });
if ($res->{success}) {
say colored(["bold cyan"], "Cluster '$name' iced - will be skipped");
say colored(["bright_black"], "Downstream clusters will run as soon as upstream complete");
} else {
say colored(["bold red"], "Failed to ice cluster: " . ($res->{message} || 'error'));
}
}
1;
lib/App/Ordo/Command/Cluster/Kill.pm view on Meta::CPAN
sub usage { "<path>" }
sub option_spec {
return {};
}
sub execute {
my ($self, $opt, $name) = @_;
unless ($name) {
say colored(["bold red"], "Usage: cluster kill <path>");
return;
}
my $payload = { name => $name };
my $res = $self->api->call('kill_cluster', $payload);
if ($res->{success}) {
say colored(["bold yellow"], "All running jobs in cluster '$name' killed");
} else {
say colored(["bold red"], "Failed to kill cluster: " . ($res->{message} || 'unknown error'));
}
}
1;
lib/App/Ordo/Command/Cluster/Melt.pm view on Meta::CPAN
sub name { "cluster melt" }
sub summary { "Unfreeze a cluster previously iced" }
sub usage { "<path/name>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $name) = @_;
unless ($name) {
say colored(["bold red"], "Usage: cluster melt <path/name>");
return;
}
my $res = $self->api->call('melt_cluster', { name => $name });
if ($res->{success}) {
say colored(["bold green"], "Cluster '$name' melted â back in rotation");
} else {
say colored(["bold red"], "Failed to melt cluster: " . ($res->{message} || 'error'));
}
}
1;
lib/App/Ordo/Command/Cluster/Release.pm view on Meta::CPAN
sub name { "cluster release" }
sub summary { "Release a held cluster" }
sub usage { "<path/name>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $name) = @_;
unless ($name) {
say colored(["bold red"], "Usage: release hold <path/name>");
return;
}
my $res = $self->api->call('release_cluster', { name => $name });
$res->{success}
? say colored(["bold yellow"], "Cluster '$name' released")
: say colored(["bold red"], "Failed: " . ($res->{message} || 'error'));
}
1;
lib/App/Ordo/Command/Cluster/Reset.pm view on Meta::CPAN
sub name { "cluster reset" }
sub summary { "Reset cluster and all jobs to ready state" }
sub usage { "<path>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $path) = @_;
unless ($path) {
say colored(["bold red"], "Usage: cluster reset <path>");
return;
}
my $res = $self->api->call('reset_cluster', { name => $path });
if ($res->{success}) {
say colored(["bold cyan"], "Cluster '$path' and all jobs reset to ready");
} else {
say colored(["bold red"], "Failed: " . ($res->{message} || 'unknown error'));
}
}
1;
lib/App/Ordo/Command/Cluster/Run.pm view on Meta::CPAN
sub name { "cluster run" }
sub summary { "Run all jobs in a cluster (in dependency order)" }
sub usage { "<path>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $path) = @_;
#unless ($path) {
# say colored(["bold red"], "Usage: cluster run <path>");
# return;
#}
my $res = $self->api->call('start_cluster', { name => $path });
if ($res->{success}) {
say colored(["bold green"], "Cluster started");
} else {
say colored(["bold red"], "Failed to start cluster: " . ($res->{message} || 'not found'));
}
}
1;
lib/App/Ordo/Command/Cluster/Show.pm view on Meta::CPAN
sub usage { "[path]" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $path) = @_;
my $res = $self->api->call('read_cluster', { name => $path });
unless ($res->{success}) {
say colored(["bold red"], "Cluster not found: $path");
say $res->{message} if $res->{message};
return;
}
my $c = $res;
$path //= $c->{name};
say colored(["bold cyan"], "Cluster: ") . colored(["bold white"], $c->{name});
say colored(["cyan"], "Path: ") . $path;
say colored(["cyan"], "State: ") . colored(["bold " . ($c->{jobstate} eq 'running' ? 'green' : 'yellow')], $c->{jobstate});
say colored(["cyan"], "Description: ") . ($c->{description} || colored(["bright_black"], "none"));
say colored(["cyan"], "Calendar: ") . ($c->{cal_name} ? $c->{cal_name} : colored(["bright_black"], "none"));
say colored(["cyan"], "Created: ") . ($c->{creation_time}
? strftime("%a %b %d %H:%M:%S %Y", localtime($c->{creation_time}))
: "unknown");
if ($c->{needs} && keys %{$c->{needs}}) {
my @all = grep { $c->{needs}{$_}{mode} == 0 } keys %{$c->{needs}};
my @any = grep { $c->{needs}{$_}{mode} == 1 } keys %{$c->{needs}};
say colored(["cyan"], "Depends on: ") . join(", ", @all) if scalar @all;
lib/App/Ordo/Command/Cluster/Show.pm view on Meta::CPAN
my $when = $ago < 3600 ? "$ago seconds ago"
: $ago < 86400 ? int($ago/3600) . " hours ago"
: int($ago/86400) . " days ago";
say colored(["cyan"], "Last run: ") . strftime("%a %b %d %H:%M:%S %Y", localtime($c->{ended}))
. colored(["green"], " ($when)");
} else {
say colored(["cyan"], "Last run: ") . colored(["yellow"], "never");
}
if ($c->{jobs} && @{$c->{jobs}}) {
say "\n" . colored(["bold green"], "Jobs in this cluster:");
my $rows = [ [qw(ID NAME SERVER STATE SCRIPT)] ];
for my $j (@{$c->{jobs}}) {
my $state_color = $j->{jobstate} eq 'complete' ? 'green' :
$j->{jobstate} eq 'running' ? 'magenta' :
$j->{jobstate} eq 'failed' ? 'red' : 'yellow';
push @$rows, [
$j->{id},
$j->{name},
$j->{server_name} || '-',
colored(["bold $state_color"], $j->{jobstate}),
$j->{script} || '(no script)',
];
}
say generate_table(rows => $rows, header_row => 1, style => 'boxrule');
} else {
say colored(["yellow"], "\nNo jobs in this cluster");
}
say "";
}
lib/App/Ordo/Command/Cluster/Update.pm view on Meta::CPAN
'loops=i' => 'Change loop count',
'clonable=i' => 'Change clonable flag',
'on_fail=s' => 'Change fail alarm cluster',
};
}
sub execute {
my ($self, $opt, $path) = @_;
unless ($path) {
say colored(["bold red"], "Usage: cluster update <path> [options]");
return;
}
my $payload = { name => $path };
if ($opt->{needs}) {
my @expanded;
for my $item (@{$opt->{needs}}) {
push @expanded, split /,/, $item;
}
lib/App/Ordo/Command/Cluster/Update.pm view on Meta::CPAN
$payload->{description} = $opt->{description} if $opt->{description};
$payload->{needs} = $opt->{needs} if $opt->{needs};
$payload->{needs_any} = $opt->{needs_any} if $opt->{needs_any};
$payload->{remove_needs} = $opt->{remove_needs} if $opt->{remove_needs};
$payload->{cal} = $opt->{cal} if exists $opt->{cal};
$payload->{loops} = $opt->{loops} if defined $opt->{loops};
$payload->{clonable} = $opt->{clonable} if defined $opt->{clonable};
$payload->{on_fail} = $opt->{on_fail} if $opt->{on_fail};
unless (keys %$payload > 1) {
say colored(["bold yellow"], "No changes specified");
return;
}
my $res = $self->api->call('update_cluster', $payload);
if ($res->{success}) {
say colored(["bold green"], "Cluster '$path' updated");
} else {
say colored(["bold red"], "Failed: " . ($res->{message} || 'unknown error'));
}
}
1;
lib/App/Ordo/Command/Exit.pm view on Meta::CPAN
sub name { "exit" }
sub summary { "Exit the interactive shell" }
sub usage { "" }
sub aliases { ['quit'] }
sub option_spec { {} }
sub execute {
my ($self) = @_;
say colored(["bold yellow"], "Goodbye!");
exit 0;
}
1;
lib/App/Ordo/Command/Help.pm view on Meta::CPAN
sub _help_topic {
my ($self, $topic) = @_;
$topic =~ s/^\s+|\s+$//g;
my @parts = split /\s+/, $topic;
my $cmd_class = "App::Ordo::Command::" . join('::', map { ucfirst lc } @parts);
if (eval "require $cmd_class; 1") {
$cmd_class->new(api => $self->api)->show_help;
} else {
say colored(["bold red"], "No help available for '$topic'");
say "Try 'help' for a list of commands";
}
}
sub _help_overview {
my ($self) = @_;
say colored(["bold cyan"], "Usage:");
say " ordo # interactive shell";
say " ordo <command> [args] # one-shot mode\n";
say colored(["bold yellow"], "Commands:\n");
my %groups = (
Navigation => [],
Jobs => [],
Clusters => [],
Calendars => [],
Servers => [],
User => [],
System => [],
);
$self->_walk_tree($self->commands, \%groups, []);
for my $group (qw(Navigation Jobs Clusters Calendars Servers User System)) {
say colored(["bold green"], " $group:");
if (@{$groups{$group}}) {
my $max = 0;
$max = length($_->[0]) > $max ? length($_->[0]) : $max for @{$groups{$group}};
printf " %-*s %s\n", $max + 4, $_->[0], $_->[1] for @{$groups{$group}};
} else {
say " (no commands)";
}
say "";
}
say colored(["bold magenta"], "Examples:");
say " server add myserver --host 192.168.1.100 --user alice";
say " cluster create mycluster";
say " job create mycluster/myjob --server myserver --script 'ls -ltr'";
say "\nType 'help <command>' for detailed help";
}
sub _walk_tree {
my ($self, $node, $groups, $path) = @_;
lib/App/Ordo/Command/Job/Complete.pm view on Meta::CPAN
sub name { "job complete" }
sub summary { "Force a job to complete state (triggers dependents)" }
sub usage { "<name>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $name) = @_;
unless ($name) {
say colored(["bold red"], "Usage: job complete <name>");
return;
}
my $res = $self->api->call('complete_job', { name => $name });
if ($res->{success}) {
say colored(["bold green"], "Job '$name' marked complete - dependents may now run");
} else {
say colored(["bold red"], "Failed: " . ($res->{message} || 'unknown error'));
}
}
1;
lib/App/Ordo/Command/Job/Create.pm view on Meta::CPAN
'clonable=i' => 'Allow cloning',
'on_fail=i' => 'Cluster to run on failure',
'json=s' => 'Extra JSON data',
};
}
sub execute {
my ($self, $opt, $name) = @_;
unless ($name && $opt->{server} && $opt->{script}) {
say colored(["bold red"], "Missing required: name, --server, --script");
$self->show_help;
return;
}
if ($opt->{needs}) {
my @expanded;
for my $item (@{$opt->{needs}}) {
push @expanded, split /,/, $item;
}
$opt->{needs} = \@expanded;
lib/App/Ordo/Command/Job/Create.pm view on Meta::CPAN
loops => $opt->{loops},
delay => $opt->{delay},
clonable => $opt->{clonable},
on_fail => $opt->{on_fail},
json => $opt->{json},
};
my $res = $self->api->call('create_job', $payload);
if ($res->{success}) {
say colored(["bold green"], "Job '$name' created on server '$opt->{server}'");
} else {
say colored(["bold red"], "Failed: " . ($res->{message} || 'unknown error'));
}
}
1;
lib/App/Ordo/Command/Job/Delete.pm view on Meta::CPAN
sub option_spec {
return {
'force|f' => 'Force delete even if running',
};
}
sub execute {
my ($self, $opt, $name) = @_;
unless ($name) {
say colored(["bold red"], "Usage: job delete <path/name> [--force]");
return;
}
my $payload = { name => $name };
$payload->{force} = 1 if $opt->{force};
my $res = $self->api->call('delete_job', $payload);
if ($res->{success}) {
say colored(["bold green"], "Job '$name' deleted");
} else {
my $msg = $res->{message} || 'unknown error';
say colored(["bold red"], "Failed to delete job: $msg");
say "Try --force if the job is running" if $msg =~ /running/i;
}
}
1;
lib/App/Ordo/Command/Job/Hold.pm view on Meta::CPAN
sub name { "job hold" }
sub summary { "Pause a job from running" }
sub usage { "<path/name>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $name) = @_;
unless ($name) {
say colored(["bold red"], "Usage: job hold <path/name>");
return;
}
my $res = $self->api->call('hold_job', { name => $name });
$res->{success}
? say colored(["bold yellow"], "Job '$name' held")
: say colored(["bold red"], "Failed: " . ($res->{message} || 'error'));
}
1;
lib/App/Ordo/Command/Job/Ice.pm view on Meta::CPAN
sub name { "job ice" }
sub summary { "Skip job without blocking dependents" }
sub usage { "<path/name>" }
sub option_spec { {} }
sub execute {
my ($self, $opt, $name) = @_;
unless ($name) {
say colored(["bold red"], "Usage: job ice <path/name>");
return;
}
my $res = $self->api->call('ice_job', { name => $name });
if ($res->{success}) {
say colored(["bold cyan"], "Job '$name' iced");
say colored(["bright_black"], "Downstream jobs will run as soon as upstream complete");
} else {
say colored(["bold red"], "Failed to ice job: " . ($res->{message} || 'error'));
}
}
1;