Skip to content

Commit

Permalink
xxx
Browse files Browse the repository at this point in the history
  • Loading branch information
rjbs committed Oct 24, 2023
1 parent 2d19d54 commit 9bcf8ad
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 121 deletions.
88 changes: 60 additions & 28 deletions lib/Synergy/Reactor/InABox.pm
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use Process::Status;
use Synergy::CommandPost;
use Synergy::Logger '$Logger';
use Synergy::Util qw(bool_from_text reformat_help);
use String::Switches qw(parse_switches);
use Synergy::SwitchBox;
use String::Switches;
use JSON::MaybeXS;
use Future::Utils qw(repeat);
use Text::Template;
Expand Down Expand Up @@ -147,20 +148,8 @@ command box => {
return await $event->error_reply(q{I didn't understand that use of "box". Check out "help box".});
}

my ($switches, $error) = parse_switches($args);
return await $event->error_reply("couldn't parse switches: $error") if $error;

my %switches = map { my ($k, @rest) = @$_; $k => \@rest } @$switches;

# This should be simplified into a more generic "validate and normalize
# switches" call. -- rjbs, 2023-10-20
for my $k (qw( version tag size )) {
next unless $switches{$k};
$switches{$k} = $switches{$k}[0];
}

eval {
await $handler->($self, $event, \%switches);
await $handler->($self, $event, $args);
};

if (my $error = $@) {
Expand All @@ -175,6 +164,38 @@ command box => {
return;
};

sub _parse_switches ($self, $args) {
my ($switches, $error) = String::Switches::parse_switches($args);
Synergy::X->throw_public("couldn't parse switches: $error") if $error;

state $switchbox = Synergy::SwitchBox->new({
schema => {
version => { type => 'str' },
tag => { type => 'str' },
size => { type => 'str' },
force => { type => 'bool' },

setup => { type => 'str', multi => 1 },
nosetup => { type => 'bool' },
},
});

my $set = eval { $switchbox->handle_switches($switches); };

return $set if $set;

my $error = $@;
if ($error isa 'Synergy::SwitchBox::Error') {
Synergy::X->throw_public({
ident => "bad-switches",
message => "Your switches were no good: \n"
. join(qq{}, map {; "* $_\n" } $error->as_sentences)
});
}

die $error;
}

sub _determine_version_and_tag ($self, $event, $switches) {
# this convoluted mess is about figuring out:
# - the version, by request or from prefs or implied
Expand All @@ -183,7 +204,9 @@ sub _determine_version_and_tag ($self, $event, $switches) {
my $default_version = $self->get_user_preference($event->from_user, 'version')
// $self->default_box_version;

my ($version, $tag) = $switches->@{qw(version tag)};
my $version = $switches->version;
my $tag = $switches->tag;

my $is_default_box = !($version || $tag);
$version //= $default_version;
$tag //= $version;
Expand All @@ -193,7 +216,10 @@ sub _determine_version_and_tag ($self, $event, $switches) {
return ($version, $tag, $is_default_box);
}

async sub handle_status ($self, $event, $switches) {
async sub handle_status ($self, $event, $args) {
return await $event->reply_error(q{"box status" doesn't take any arguments.})
if length $args;

my $droplets = await $self->_get_droplets_for($event->from_user);

if (@$droplets) {
Expand All @@ -206,20 +232,21 @@ async sub handle_status ($self, $event, $switches) {
return await $event->reply("You don't seem to have any boxes.");
}

async sub handle_create ($self, $event, $switches) {
async sub handle_create ($self, $event, $args) {
my $switches = $self->_parse_switches($args);
my ($version, $tag, $is_default_box) = $self->_determine_version_and_tag($event, $switches);

# XXX call /v2/sizes API to validate
# https://developers.digitalocean.com/documentation/changelog/api-v2/new-size-slugs-for-droplet-plan-changes/
my $size = $switches->{size} // $self->default_box_size;
my $size = $switches->size // $self->default_box_size;
my $user = $event->from_user;

if ($switches->{setup} && $switches->{nosetup}) {
if ($switches->has_setup && $switches->nosetup) {
Synergy::X->throw_public("Passing /setup and /nosetup together is too weird for me to handle.");
}

my $should_run_setup = $switches->{setup} ? 1
: $switches->{nosetup} ? 0
my $should_run_setup = $switches->has_setup ? 1
: $switches->nosetup ? 0
: $self->get_user_preference($user, 'setup-by-default');

my $maybe_droplet = await $self->_get_droplet_for($user, $tag);
Expand Down Expand Up @@ -320,7 +347,7 @@ async sub handle_create ($self, $event, $switches) {
$event,
$droplet,
$key_file,
$switches->{setup},
[ $switches->setup ],
);
}

Expand Down Expand Up @@ -430,7 +457,8 @@ async sub _setup_droplet ($self, $event, $droplet, $key_file, $args = []) {
return await $event->reply("Something went wrong setting up your box, sorry!");
}

async sub handle_destroy ($self, $event, $switches) {
async sub handle_destroy ($self, $event, $args) {
my $switches = $self->_parse_switches($args);
my ($version, $tag) = $self->_determine_version_and_tag($event, $switches);

my $droplet = await $self->_get_droplet_for($event->from_user, $tag);
Expand All @@ -441,7 +469,7 @@ async sub handle_destroy ($self, $event, $switches) {
);
}

if ($droplet->{status} eq 'active' && !$switches->{force}) {
if ($droplet->{status} eq 'active' && !$switches->force) {
Synergy::X->throw_public(
"That box is powered on. Shut it down first, or use /force to destroy it anyway."
);
Expand Down Expand Up @@ -530,25 +558,29 @@ async sub _handle_power ($self, $event, $action, $tag = undef) {
return;
}

sub handle_shutdown ($self, $event, $switches) {
sub handle_shutdown ($self, $event, $args) {
my $switches = $self->_parse_switches($args);
my ($version, $tag) = $self->_determine_version_and_tag($event, $switches);

return $self->_handle_power($event, 'shutdown', $tag);
}

sub handle_poweroff ($self, $event, $switches) {
sub handle_poweroff ($self, $event, $args) {
my $switches = $self->_parse_switches($args);
my ($version, $tag) = $self->_determine_version_and_tag($event, $switches);

$self->_handle_power($event, 'off', $tag);
}

sub handle_poweron ($self, $event, $switches) {
sub handle_poweron ($self, $event, $args) {
my $switches = $self->_parse_switches($args);
my ($version, $tag) = $self->_determine_version_and_tag($event, $switches);

return $self->_handle_power($event, 'on', $tag);
}

sub handle_vpn ($self, $event, $switches) {
sub handle_vpn ($self, $event, $args) {
my $switches = $self->_parse_switches($args);
my ($version, $tag) = $self->_determine_version_and_tag($event, $switches);

my $template = Text::Template->new(
Expand Down
27 changes: 25 additions & 2 deletions lib/Synergy/SwitchBox.pm
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,26 @@ sub _build_switchset_class ($self) {
handles => { $name => 'elements' },
default => sub { [] },
);

# You might think "this is absurd, this should be a predicate method on
# the $name attribute, but we have a conundrum:
# * We want $obj->attr to return () if no "attr" was given, and this
# requires that attr is [], because it returns @{ $attr_slot }.
# * We want to know that no value was present.
#
# Since we need to have a default [] value for the "elements" delegation
# to work, we'll store "was explicit" in another attribute. Since we
# know we're the only one making these objects, we can get away with it.
# Gross, though, I know! -- rjbs, 2023-10-22
$meta->add_attribute("has_$name", is => 'ro');

next ATTR;
}

$meta->add_attribute($name,
is => 'ro',
isa => 'Value',
predicate => "has_$name",
);
}

Expand Down Expand Up @@ -106,6 +120,7 @@ sub _check_and_coerce_values ($self, $schema, $values) {

sub handle_switches ($self, $switches) {
my %switch;
my %predicate;

my %error;
# return SwitchBox::Set if good
Expand Down Expand Up @@ -137,6 +152,8 @@ sub handle_switches ($self, $switches) {
if (@args == 0) {
if ($schema->{type} eq 'bool') {
@args = 1;
} elsif ($schema->{multi} && $schema->{zero_ok}) {
# ... just let it become set ...
} else {
$error{switch}{$name}{novalue} = 1;
next SWITCH;
Expand All @@ -154,6 +171,7 @@ sub handle_switches ($self, $switches) {
if ($schema->{multi}) {
$switch{$name} //= [];
push $switch{$name}->@*, @args;
$predicate{"has_$name"} = 1;
next SWITCH;
}

Expand All @@ -173,7 +191,12 @@ sub handle_switches ($self, $switches) {
Synergy::SwitchBox::Error->throw({ errors => \%error });
}

return $self->switchset_class->new(\%switch);
# Maybe later: barf, now or much earlier, if there's a multi-value switch S
# and also any switch named has_S. Honestly, though... -- rjbs, 2023-10-22
return $self->switchset_class->new({
%switch,
%predicate,
});
}

package Synergy::SwitchBox::Error {
Expand Down Expand Up @@ -259,7 +282,7 @@ package Synergy::SwitchBox::Error {

if (keys %switch_unknown) {
push @sentences,
"There was something inexplicably wrong with these switches: " . andlist(\%switch_multi) . ".";
"There was something inexplicably wrong with these switches: " . andlist(\%switch_unknown) . ".";
}

if (%other) {
Expand Down
19 changes: 17 additions & 2 deletions t/lib/Test/SwitchBox.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package Test::SwitchBox;
use Moose;
extends 'Synergy::SwitchBox';

use experimental qw(signatures);
use experimental qw(isa signatures);

use String::Switches ();
use Test::Deep ':v1';
Expand All @@ -18,8 +18,19 @@ sub switches_ok ($self, $str, $want, $desc) {

local $Test::Builder::Level = $Test::Builder::Level + 1;

my $set;

subtest "switches_ok: $desc" => sub {
my $set = $self->handle_switches($switches);
$set = eval {
$self->handle_switches($switches);
};

if ($@ and $@ isa 'Synergy::SwitchBox::Error') {
my $error = $@;
fail("SwitchBox rejected the input");
diag(explain([ $error->as_structs ]));
return;
}

isa_ok($set, 'Synergy::SwitchBox::Set', 'result of handle_switches');

Expand All @@ -39,6 +50,8 @@ sub switches_ok ($self, $str, $want, $desc) {
"methods on SwitchBox::Set act as expected",
);
};

return $set;
}

sub errors_ok ($self, $str, $want, $desc) {
Expand Down Expand Up @@ -70,6 +83,8 @@ sub errors_ok ($self, $str, $want, $desc) {
);
}

diag "S: $_" for $error->as_sentences;

if ($want->{sentences}) {
my @sentences = $error->as_sentences;

Expand Down
Loading

0 comments on commit 9bcf8ad

Please sign in to comment.