App-FargateStack
view release on metacpan or search on metacpan
lib/App/FargateStack/Builder/Autoscaling.pm view on Meta::CPAN
if ( $autoscaling_config->has_scheduled_action ) {
$self->build_scheduled_actions(
task => $task,
resource_id => $resource_id,
app_autoscaling => $app_autoscaling,
autoscaling_config => $autoscaling_config,
);
}
my $scaling_policy = $app_autoscaling->describe_scaling_policies(
service_namespace => 'ecs',
policy_names => $policy_name,
query => 'ScalingPolicies',
);
$app_autoscaling->check_result( message => 'ERROR: could not describe scaling policies for: [%s]', $resource_id );
$scaling_policy = $scaling_policy->[0];
return
if !$autoscaling_config->get_cpu && !$autoscaling_config->get_requests;
my $policy_configuration = $self->create_policy_configuration( $task_name, $autoscaling_config );
if ( !$scaling_policy ) {
$self->log_warn( 'autoscaling: policy: [%s] does not exist...will be created...%s', $policy_name, $dryrun );
$self->inc_required_resources(
'autoscaling:scaling-policy' => sub {
my ($dryrun) = @_;
return $dryrun ? 'arn:???' : $autoscaling_config->get_policy_arn;
}
);
if ( !$dryrun ) {
my $arn = $self->create_autoscaling_policy(
app_autoscaling => $app_autoscaling,
resource_id => $resource_id,
min_capacity => $autoscaling_config->get_min_capacity,
max_capacity => $autoscaling_config->get_max_capacity,
policy_name => $policy_name,
policy_configuration => $policy_configuration,
);
$autoscaling_config->update( policy_arn => $arn );
}
return $TRUE;
}
my $current_policy = $scaling_policy->{TargetTrackingScalingPolicyConfiguration};
my $diffs = $self->display_diffs( $policy_configuration, $current_policy );
if ($diffs) {
$self->log_error( "autoscaling: scaling policy: [%s] has changed:\n%s\n", $policy_name, $diffs );
log_die( $self, 'autoscaling: aborting update...use --force to force update' )
if !$self->get_force;
$self->log_warn( 'autoscaling: scaling policy: [%s] will be updated...%s', $policy_name, $dryrun );
if ( !$dryrun ) {
my $arn = $self->create_autoscaling_policy(
app_autoscaling => $app_autoscaling,
resource_id => $resource_id,
min_capacity => $autoscaling_config->get_min_capacity,
max_capacity => $autoscaling_config->get_max_capacity,
policy_name => $policy_name,
policy_configuration => $policy_configuration,
);
$autoscaling_config->update( policy_arn => $arn ); # the arn should not change, but why not?
}
}
else {
$self->log_info( 'autoscaling: scaling policy: [%s] has not changed...skipping', $policy_name );
$self->inc_existing_resources( autoscaling => $autoscaling_config->get_policy_arn );
}
return $TRUE;
}
########################################################################
sub _resource_id {
########################################################################
my ( $self, $task_name ) = @_;
return sprintf 'service/%s/%s', $self->get_config->{cluster}->{name}, $task_name;
}
########################################################################
sub create_policy_configuration {
########################################################################
my ( $self, $task_name, $autoscaling_config ) = @_;
my $scale_in_cooldown = $autoscaling_config->get_scale_in_cooldown;
my $scale_out_cooldown = $autoscaling_config->get_scale_out_cooldown;
my $metric = $autoscaling_config->get_metric;
my $metric_value = $autoscaling_config->get_metric_value;
my $predefined_metric_type = {
cpu => 'ECSServiceAverageCPUUtilization',
requests => 'ALBRequestCountPerTarget',
}->{$metric};
my $resource_label = $metric eq 'requests' ? $self->create_resource_label($task_name) : q{};
my $predefined_metric_specficiation = {
PredefinedMetricType => $predefined_metric_type,
$resource_label ? ( ResourceLabel => $resource_label ) : ()
};
my $policy_configuration = {
TargetValue => $metric_value,
PredefinedMetricSpecification => $predefined_metric_specficiation,
ScaleOutCooldown => $scale_out_cooldown,
ScaleInCooldown => $scale_in_cooldown,
};
lib/App/FargateStack/Builder/Autoscaling.pm view on Meta::CPAN
# configured actions, could be new or existing
######################################################################
my @actions = keys %{$parsed_actions};
######################################################################
# fetch existing scheduled actions
######################################################################
my $schedules = $app_autoscaling->describe_scheduled_actions( service_namespace => 'ecs' );
$app_autoscaling->check_result( message => 'ERROR: could not describe scheduled actions' );
my $scheduled_actions = { map { ( $_->{ScheduledActionName} => $_ ) } @{ $schedules->{ScheduledActions} // [] } };
my @scheduled_action_names = keys %{$scheduled_actions};
######################################################################
# collect new scheduled actions...
######################################################################
my %put_scheduled_actions;
foreach my $action_name (@actions) {
my $action = $parsed_actions->{$action_name};
if ( none { $_ =~ /$action_name/xsm } @scheduled_action_names ) {
$self->log_warn( 'autoscaling: schedule action: [%s] does not exist...will be created...%s', $action_name, $dryrun );
$self->inc_required_resources( 'autoscaling:scheduled-action' => $action_name );
$self->log_debug( sub { return Dumper( [ action => $action ] ) } );
$put_scheduled_actions{$action_name} = $action;
next;
}
my @existing_actions = grep { $_ =~ /$action_name/xsm } @scheduled_action_names;
foreach (@existing_actions) {
my $target_action = $scheduled_actions->{$_};
my $min_capacity = $target_action->{ScalableTargetAction}->{MinCapacity};
my $max_capacity = $target_action->{ScalableTargetAction}->{MaxCapacity};
my $schedule = $target_action->{Schedule};
my $scale_type = $target_action->{ScheduledActionName} =~ /\-in\-/xsm ? 'ScaleIn' : 'ScaleOut';
if ( $schedule eq $action->{$scale_type}->{Schedule}
&& $min_capacity == $action->{$scale_type}->{Action}->{MinCapacity}
&& $max_capacity == $action->{$scale_type}->{Action}->{MaxCapacity} ) {
$self->log_info( 'autoscaling: scheduled action: [%s] for [%s] has not changed...skipping', $action_name, $scale_type );
$self->inc_existing_resources(
'autoscaling:scheduled' => [
join "\n",
sprintf '%s min_capacity:%s max_capacity: %s', $target_action->{Schedule},
$target_action->{ScalableTargetAction}->{MinCapacity},
$target_action->{ScalableTargetAction}->{MaxCapacity}
]
);
}
else {
$self->log_warn( 'autoscaling: scheduled action: [%s] for [%s] has changed...will be updated...%s',
$action_name, $scale_type, $dryrun );
$self->inc_required_resources( 'autoscaling:scheduled-action' => $action_name );
$put_scheduled_actions{$action_name} = $action;
}
}
}
######################################################################
# Sanity check the top-level metric based capacities and scheduled actions min/max capacities
######################################################################
if ( !$autoscaling_config->get_cpu && !$autoscaling_config->get_requests ) {
####################################################################
# Rule: 1 If no metric scaling action exists, min/max should be equal
####################################################################
my $error_msg = <<'END_OF_ERROR_MESSAGE';
ERROR: Configuration for schedule '%s' is inconsistent.
It defines a min_capacity (%d) that is different from its max_capacity (%d)
for its 'in' period, but no metric-based scaling policy (cpu or requests) has been defined.
Without a metric policy, the service can only scale to the minimum capacity and will never reach the maximum.
To resolve this, please set min_capacity and max_capacity to the same value for a fixed-capacity schedule.
END_OF_ERROR_MESSAGE
foreach my $p ( pairs %put_scheduled_actions ) {
if ( $p->[1]->{ScaleOut}->{Action}->{MinCapacity} != $p->[1]->{ScaleOut}->{Action}->{MaxCapacity} ) {
$self->log_die(
$error_msg, $p->[0],
$p->[1]->{ScaleOut}->{Action}->{MinCapacity},
$p->[1]->{ScaleOut}->{Action}->{MaxCapacity}
);
}
if ( $p->[1]->{ScaleIn}->{Action}->{MinCapacity} != $p->[1]->{ScaleIn}->{Action}->{MaxCapacity} ) {
$self->log_die(
$error_msg, $p->[0],
$p->[1]->{ScaleIn}->{Action}->{MinCapacity},
$p->[1]->{ScaleIn}->{Action}->{MaxCapacity}
);
}
}
# presumbably existing schedules are sane...but we will check anyway
foreach (@scheduled_action_names) {
my $min_capacity = $scheduled_actions->{$_}->{ScalableTargetAction}{MinCapacity};
my $max_capacity = $scheduled_actions->{$_}->{ScalableTargetAction}{MaxCapacity};
log_die( $self, $error_msg, $min_capacity, $max_capacity )
if $min_capacity != $max_capacity;
}
}
else {
####################################################################
# Rule 2: top-level max_capacity >= scheduled action max capacity
####################################################################
my $error_msg = <<'END_OF_ERROR_MESSAGE';
ERROR: Configuration for a scheduled action is inconsistent.
It defines a max_capacity (%d) that is greater than the top-level max_capacity (%d).
In App::FargateStack, the top-level 'max_capacity' is the absolute ceiling for the service at all times.
To resolve this, please set the top-level 'max_capacity' to the highest value your service should ever scale to (%d or greater).
END_OF_ERROR_MESSAGE
( run in 1.419 second using v1.01-cache-2.11-cpan-e1769b4cff6 )