App-FargateStack

 view release on metacpan or  search on metacpan

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

  }

  # more than 1, error or we found just 1
  return $tasks[1] ? $EMPTY : $tasks[0];
}

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

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

  my $task_name = $self->get_default_task_name('task');

  die sprintf "usage: %s run-task task-name\n", $ENV{SCRIPT_NAME}
    if !$task_name;

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

  log_die( $self, "ERROR: no such task [%s] defined in config\n", $task_name )
    if !$task;

  log_die( $self, "ERROR: [%s] is not a task\n", $task_name )
    if $task->{type} ne 'task';

  my $subnet_id = $self->get_subnet_id;
  my $is_public = $FALSE;

  if ( !$subnet_id ) {
    my @subnets = @{ $self->get_subnets->{private} // [] };

    if ( !@subnets ) {
      $self->log_warn('run-task: using public subnets is not recommended...');
      @subnets = @{ $self->get_subnets->{public} // [] };
    }

    $subnet_id = $subnets[0];
  }
  elsif ( any { $subnet_id eq $_ } @{ $self->get_subnets->{public} // [] } ) {
    $self->log_error( 'run-task: subnet-id: [%s] is in a public subnet...consider running your jobs in a private subnet',
      $subnet_id );
    $is_public = $TRUE;
  }
  elsif ( none { $subnet_id eq $_ } @{ $self->get_subnets->{private} // [] } ) {
    log_die( $self, 'subnet: [%s] is not in a public or private subnet in this VPC.', $subnet_id );
  }

  my $network_configuration = {
    awsvpcConfiguration => {
      subnets        => [$subnet_id],
      securityGroups => [ $security_groups->{fargate}->{group_id} ],
      assignPublicIp => $is_public ? 'ENABLED' : 'DISABLED',
    }
  };

  # check for latest image...
  $self->check_latest_image($task_name);

  # this may be null if we are in dryrun mode and the config has not been updated
  my $cluster_name
    = $dryrun && !$cluster->{name}
    ? sprintf '%s-cluster', $config->{app}->{name}
    : $cluster->{name};

  $self->log_warn( 'run-task: cluster: [%s] launching task: [%s] in subnet: [%s]...%s',
    $cluster_name, $task_name, $subnet_id, $dryrun );

  $self->log_trace( sub { return Dumper( [ awsvpcConfiguration => $network_configuration ] ); } );

  return $SUCCESS
    if $dryrun;

  log_die( $self, 'run-task: cluster has not been created yet...run "apply" first' )
    if !$cluster_name;

  my $ecs = $self->fetch_ecs;

  my $result = $self->get_ecs->run_task(
    cluster               => $cluster->{name},
    task_definition       => $task_name,
    network_configuration => $network_configuration,
  );

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

  my @failures = @{ $result->{failures} };

  log_die( $self, 'ERROR: task failed to launch: %s', Dumper( \@failures ) )
    if @failures;

  ($tasks) = @{ $result->{tasks} };

  my $task_arn = $tasks->{taskArn};

  my $should_wait = $self->get_wait ? '(waiting)' : $EMPTY;

  $self->log_warn( 'run-task: task [%s] launched. ARN: [%s]...%s', $task_name, $task_arn, $should_wait );

  my $poll_limit = $self->get_task_timeout / $DEFAULT_ECS_POLL_TIME;

  if ($should_wait) {
    my $poll_count = 0;

    while ( $poll_count++ < $poll_limit ) {

      my ( $status, $stopped_reason, $exit_code ) = $self->get_task_status( $cluster_name, $task_arn );

      $self->log_warn( 'run-task: task [%s] status: [%s], exit code:[%s], reason: [%s]',
        $task_name, map { $_ // q{-} } ( $status, $exit_code, $stopped_reason ) );

      last if $status eq 'STOPPED';

      sleep $DEFAULT_ECS_POLL_TIME;
    }

    my $log_group = $config->{log_group}->{name};

    # by convention our log groups are named after our app

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


########################################################################
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));

  my $task_name = $self->get_default_task_name;

  my $action = $self->get_skip_register ? 'update-target' : 'register';

  log_die( $self, 'usage: %s %s task-name', $action, $ENV{SCRIPT_NAME} )
    if !$task_name;

  my $task_definition_file = sprintf 'taskdef-%s.json', $task_name;

  $self->check_task($task_name);

  log_die( $self, "ERROR: no task definition file found for %s\n", $task_name )
    if !-s $task_definition_file;

  my $task_definition_arn;

  my $ecs = $self->fetch_ecs;

  if ( !$self->get_skip_register ) {
    $self->log_warn( 'register: registering task definition for: [%s]...%s', $task_name, $dryrun );

    if ( !$dryrun ) {
      my $task_definition = $ecs->register_task_definition($task_definition_file);
      $ecs->check_result( message => 'ERROR: register: could not register [%s]\n%s', $task_definition_file );
      $self->log_trace( sub { return Dumper( [ task_definition => $task_definition ] ) } );

      $task_definition_arn = $task_definition->{taskDefinition}->{taskDefinitionArn};

      log_die( $self, 'register: no taskDefinitionArn found? %s', Dumper( [ task_definition => $task_definition ] ) )
        if !$task_definition_arn;

      $self->log_warn( 'register: registered...[%s]', $task_definition_arn );

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

      my $latest_image = $self->get_latest_image($task_name);

      $self->log_info( 'register: updating image digest: [%s]', $latest_image->{imageDigest} );
      $tasks->{$task_name}->{image_digest} = $latest_image->{imageDigest};

      $self->update_config;  # record new task definition arn
    }
  }

  ## - events -
  if ( $tasks->{$task_name}->{type} eq 'task' ) {
    require App::Events;

    my $event = $self->fetch_events;

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

END_OF_ERROR
    print {*STDERR} colored( $msg, 'bright_red' );
  }

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

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

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

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

  die "usage: $ENV{SCRIPT_NAME} delete-http task-name\n"
    if !$task_name || $err;

  die "ERROR: [$task_name] is not an http task"
    if !$tasks->{$task_name}->{type} =~ /^https?/xsm;

  if ( scalar keys %{$tasks} == 1 ) {
    my $msg = <<'END_OF_WARNING';
WARNING: This is the only task in your configuration.

 - You can stop the http service from running with the "stop-service" command.
 - You can delete only the service with the "delete-service" command.

END_OF_WARNING
    print {*STDERR} colored( $msg, 'bright_red' );
  }

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

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

  my $cluster      = $self->common_args('cluster');
  my $cluster_name = $cluster->{name};

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

  die sprintf "usage: %s redeploy service-name\n", $ENV{SCRIPT_NAME}
    if !$service_name || $err;

  my $ecs = $self->get_ecs;

  my $result = $ecs->update_service(
    cluster_name => $cluster_name,
    service_name => $service_name,
    force        => $TRUE
  );

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

  $self->log_info( 'redeploy: successfully updated service: [%s]', $service_name );

  return $SUCCESS;
}

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

  $self->fetch_option_defaults($TRUE);

  return $SUCCESS;
}

########################################################################
sub fetch_option_defaults {
########################################################################
  my ( $self, $reset ) = @_;

  my $options = {};

  my $defaults_file = '.fargatestack/defaults.json';

  if ( -s $defaults_file ) {
    $options = slurp_file( $defaults_file, $TRUE );
  }
  else {
    mkdir '.fargatestack';
  }

  return $self->write_json_file( $defaults_file, {} )
    if $reset;

  $options->{profile}         = $self->get_profile     // $options->{profile};
  $options->{config}          = $self->get_config_name // $options->{config};
  $options->{region}          = $self->default_region( $options->{region} );
  $options->{route53_profile} = $self->get_route53_profile // $options->{route53_profile};
  $options->{max_events}      = $self->get_max_events      // $options->{max_events};

  $self->set_profile( $options->{profile} );
  $self->set_config_name( $options->{config} );
  $self->set_route53_profile( $options->{route53_profile} );
  $self->set_max_events( $options->{max_events} );

  $self->write_json_file( $defaults_file, $options );

  return $options;
}

########################################################################
sub build_section_paths {
########################################################################
  my @items = @_;  # list of strings like '1:Title'

  my %paths;
  my @stack;

  foreach my $line (@items) {
    next if $line !~ /^(\d+):(.*)$/xsm;



( run in 0.632 second using v1.01-cache-2.11-cpan-e1769b4cff6 )