App-FargateStack

 view release on metacpan or  search on metacpan

lib/App/FargateStack.pm  view on Meta::CPAN


    $path = realpath($path);

    my $fqp = sprintf '%s/%s%s', $path, $name, $ext || 'yml';

    die sprintf "ERROR: file not found: [%s]\n", $fqp
      if !-s $fqp;

    $options->{config} = $fqp;

    $self->write_json_file( $defaults_file, $options );
    $config_name = $name;
  }

  my $data = [
    { Profile       => $options->{profile},
      'DNS Profile' => $options->{route53_profile},
      'Max Events', => $options->{max_events},
      'Config'      => $options->{config},
      'Region'      => $options->{region},
    }
  ];

  my @columns = ( 'Profile', 'DNS Profile', 'Region', 'Config', 'Max Events' );

  print {*STDOUT} easy_table(
    table_options => { headingText => sprintf 'Current Defaults: %s', $config_name },
    columns       => \@columns,
    data          => $data,
  );

  return $SUCCESS;
}

########################################################################
sub cmd_show {
########################################################################
  my ( $self, @args ) = @_;

  my ($command) = $self->get_args;

  my %sub_commands = ( 'cloudtrail-events' => \&cmd_cloudtrail_events, );

  die sprintf "ERROR: not a valid command. Must be one of: \n\t%s\n", join "\n\t", keys %sub_commands
    if !$sub_commands{$command};

  return $sub_commands{$command}->($self);
}

########################################################################
sub cmd_service_status {
########################################################################
  my ( $self, @args ) = @_;

  my $service_name = $self->check_service_name( @args, $self->get_args );

  my ( $cluster, $tasks ) = $self->common_args(qw(cluster tasks));

  my $cluster_name = $cluster->{name};

  $self->verify_service($service_name);

  require Text::Wrap;
  Text::Wrap->import('wrap');

  {
    ## no critic
    no warnings 'once';
    $Text::Wrap::columns = 100;
  }

  my @elems = qw(running_count desired_count status pending_count events task_definition);

  my $query = jmespath_mapping 'services[0]' => \@elems;
  my $ecs   = $self->fetch_ecs;

  my $result = $ecs->describe_services(
    cluster_name => $cluster_name,
    service_name => $service_name,
    query        => $query,
  );

  log_die( $self, "ERROR: could not describe service [%s]\n%s", $service_name, $self->get_ecs->get_error )
    if !$result;

  my ( $running_count, $desired_count, $status, $pending_count, $events, $task_definition_arn )
    = @{$result}{@elems};

  $pending_count //= q{-};

  $status = $self->maybe_color( $status eq 'ACTIVE' ? 'bright_green' : 'bright_yellow' => $status );

  my $title = sprintf "Service:[%s]\n", $self->maybe_color( bright_white => $service_name );

  $title .= sprintf "Status:[%s] Running:[%s] Pending:[%s] Desired:[%s]\n",
    $status,
    $self->maybe_color( green        => $running_count ),
    $self->maybe_color( yellow       => $pending_count ),
    $self->maybe_color( bright_white => $desired_count );

  $title .= sprintf 'Task Definition: [%s]', $self->maybe_color( 'bright_white' => $task_definition_arn );

  my @events = grep {defined} @{ $result->{events} }[ 0 .. ( $self->get_max_events - 1 ) ];

  my @data = map { { 'Time' => $_->{createdAt}, Event => wrap( q{}, q{}, $_->{message} ) } } @events;

  print {*STDOUT} easy_table(
    table_options => { headingText => $title, allowANSI => $TRUE },
    data          => \@data,
    columns       => [qw(Time Event)],
  );

  $self->display_task_status($service_name);

  return $SUCCESS;
}

########################################################################
sub display_task_status {
########################################################################
  my ( $self, $service_name ) = @_;

lib/App/FargateStack.pm  view on Meta::CPAN

    ( $task_name, my $err ) = $self->get_default_service_name($TRUE);  # skip arg

    if ( $err || !$task_name ) {
      if ($err) {
        $self->log_error('ERROR: multiple services in configuration.');
      }
      elsif ( !$task_name ) {
        $self->log_error( 'ERROR: %s',
          $task_name ? "service: [$task_name] not found in configuration" : 'no service types in configuration' );
      }

      die sprintf "usage: %s deploy-service service-name\n", $ENV{SCRIPT_NAME};
    }
  }

  if ( !$desired_count || !looks_like_number $desired_count ) {
    $desired_count = $tasks->{$task_name}->{desired_count} // 1;
  }

  $self->log_info('service: checking to see if task and latest image are aligned...');

  $self->check_latest_image($task_name);

  return $self->build_service( $task_name, $desired_count );
}

########################################################################
sub check_task {
########################################################################
  my ( $self, $task_name, $warn ) = @_;

  my $level = $warn ? 'warn' : 'die';

  my $config = $self->get_config;

  return $TRUE
    if $task_name && $config->{tasks}->{$task_name};

  log_die( $self, 'ERROR:  no such task [%s] defined in config', $task_name )
    if $level eq 'die';

  $self->get_logger->warn( 'WARNING: no such task [%s] defined in config...trying anyway  ¯\_(ツ)_/¯', $task_name );

  return;
}

########################################################################
sub cmd_remove_service {
########################################################################
  my ($self) = @_;

  my ( $task_name, $err ) = $self->get_default_service_name();

  die "usage: %s remove-service task-name\n", $ENV{SCRIPT_NAME}
    if !$task_name || $err;

  my ( $config, $cluster, $dryrun ) = $self->common_args(qw(config cluster dryrun));

  my $cluster_name = $cluster->{name};

  $self->verify_service($task_name);

  $self->check_task( $task_name, 'warn' );

  $self->log_warn( 'remove-service: task [%s] will be deleted...%s', $task_name, $dryrun );

  return $SUCCESS
    if $dryrun;

  my $ecs = $self->fetch_ecs;

  my $result = $ecs->delete_service( $cluster->{name}, $task_name );
  $ecs->check_result( message => 'ERROR: could not stop service %s', $task_name );

  return $SUCCESS;
}

########################################################################
sub verify_service {
########################################################################
  my ( $self, $service_name ) = @_;

  my ( $cluster, $config ) = $self->common_args(qw(cluster config));

  my $cluster_name //= $cluster->{name};

  my $ecs = $self->fetch_ecs;

  my $services = $ecs->list_services( $cluster_name, 'serviceArns' );

  die sprintf "ERROR: could not list services for cluster: [%s]\n%s", $cluster_name, $ecs->get_error
    if !$services;

  die sprintf "ERROR: no services running in cluster: [%s]\n", $cluster_name
    if !@{$services};

  die sprintf "ERROR: service [%s] is not running in cluster: [%s]\n", $service_name, $cluster_name
    if none { $_ =~ /$service_name/xsm } @{$services};

  return;
}

########################################################################
sub get_task_image_digests {
########################################################################
  my ( $self, $task_name ) = @_;
  my $ecs = $self->fetch_ecs;

  my $cluster_name = $self->get_config->{cluster}->{name};

  my $task_arns = $ecs->list_tasks( $cluster_name, 'taskArns' );
  $ecs->check_result( message => 'ERROR: Could not list tasks for cluster: [%s]', $cluster_name );

  my $group = sprintf 'service:%s', $task_name;

  my $query = sprintf 'tasks[?group == `%s`].containers[].{imageDigest:imageDigest}[].imageDigest', $task_name;

  my $image_digests = $ecs->describe_tasks( $cluster_name, $task_arns, $query );
  $ecs->check_result( message => 'ERROR: could not describe tasks for task arns: [%s]', join q{,}, @{$task_arns} );

  return $image_digests;
}

########################################################################
sub get_latest_task_definition {
########################################################################
  my ( $self, $task_name ) = @_;

  my $tasks = $self->get_config->{tasks};

  my $task_definition_arn = $tasks->{$task_name};

  my $ecs = $self->fetch_ecs;

  my $task_definitions = $ecs->list_task_definitions( $task_name, 'taskDefinitionArns' );
  $ecs->check_result( message => 'ERROR: could list task definitions for [%s]', $task_name );

  my ($latest_task_definition) = sort {
    my ($num_a) = $a =~ /:(\d+)$/xsm;
    my ($num_b) = $b =~ /:(\d+)$/xsm;
    $num_b <=> $num_a
  } @{$task_definitions};

  my ($latest_task_definition_version) = $latest_task_definition =~ /(:\d+)$/xsm;
  my ($task_definition_version)        = $task_definition_arn    =~ /(:\d+)$/xsm;

  return $latest_task_definition,;
}

########################################################################
sub cmd_update_service {
########################################################################
  my ( $self, @args ) = @_;

  my $service_name = $self->check_service_name( @args, $self->get_args );

  my ($cluster) = $self->common_args(qw(cluster));

  my $cluster_name = $cluster->{name};

  $self->verify_service($service_name);

  my $ecs = $self->fetch_ecs;

  my $task_definition_arn = $self->get_latest_task_definition($service_name);

  my @elems = qw(status running_count desired_count pending_count task_definition);

  my $result = $ecs->update_service(
    cluster_name    => $cluster_name,
    service_name    => $service_name,
    task_definition => basename($task_definition_arn),
    query           => jmespath_mapping( service => \@elems ),
  );

  $ecs->check_result( message => 'ERROR: could not update service: [%s]', $service_name );

  $self->log_debug( sub { return Dumper( [ result => $result ] ) } );

  my @data = {
    'Status'        => $self->maybe_color( bright_white => $result->{status} ),
    'Running Count' => $self->maybe_color( green        => $result->{running_count} ),
    'Desired Count' => $self->maybe_color( bright_white => $result->{desired_count} ),
    'Pending Count' => $self->maybe_color( yellow       => $result->{pending_count} ),
  };

  print {*STDOUT} easy_table(
    table_options => {
      allowANSI   => $TRUE,
      headingText => sprintf "Service Status\nTask Definition: %s",
      $result->{task_definition}
    },
    data    => \@data,
    columns => [ 'Status', 'Running Count', 'Pending Count', 'Desired Count' ]
  );

  print {*STDOUT} <<'END_OF_NOTE';
* Note that this command will not force redeployment of your services! To force redeployment of your servicce:

 app-FargateStack redeploy [service-name]

END_OF_NOTE

  return;
}

########################################################################
sub update_task_count {
########################################################################
  my ( $self, $task_name, $desired_count ) = @_;

  my ( $config, $cluster ) = $self->common_args(qw(config cluster));

  my $cluster_name = $cluster->{name};

  $self->verify_service($task_name);

  my $ecs = $self->get_ecs;

  my $result = $ecs->update_service(
    cluster_name  => $cluster_name,
    desired_count => $desired_count,
    service_name  => $task_name,
  );

  log_die( $self, "ERROR: could not update service: [%s]\n%s", $task_name, $ecs->get_error )
    if !$result;

  return $result;
}

########################################################################
sub cmd_start_stop_service {
########################################################################
  my ($self) = @_;

  my ( $task_name, $count ) = $self->get_args;

  if ( looks_like_number $task_name ) {
    $count     = $task_name;
    $task_name = $EMPTY;
  }

  $task_name = $self->check_service_name($task_name);
  my $command = $self->command;

  if ( $command eq 'start-service' ) {
    $count ||= 1;
  }
  elsif ( $command eq 'update-service' ) {
  }
  else {
    $count = 0;
  }

  if ( !$task_name ) {
    if ( $count == 0 ) {
      die sprintf "usage: %s -c config-name stop-service task-name\n", $ENV{SCRIPT_NAME};
    }

    die sprintf "usage: %s -c config-name start-service task-name [count]\n", $ENV{SCRIPT_NAME};
  }

  my $result = $self->update_task_count( $task_name, $count );

  sleep 2;  # wait a few seconds for status to be updated

  return $self->cmd_service_status($task_name);
}

########################################################################
sub cmd_register_task_definition {
########################################################################
  my ($self) = @_;

  my ( $config, $tasks, $dryrun ) = $self->common_args(qw(config tasks dryrun));



( run in 0.983 second using v1.01-cache-2.11-cpan-13bb782fe5a )