Skip to content

Add ECS service scaling command#18

Merged
meap merged 3 commits intomainfrom
meap/scale
Aug 16, 2025
Merged

Add ECS service scaling command#18
meap merged 3 commits intomainfrom
meap/scale

Conversation

@meap
Copy link
Copy Markdown
Owner

@meap meap commented Aug 16, 2025

Summary

  • Implemented new scale command to adjust ECS service task counts
  • Added validation to prevent scaling to 0 tasks (minimum is 1, maximum is 1000)
  • Uses AWS ECS UpdateService API for direct scaling without task sets

Implementation Details

  • Created internal/ecs/scale.go with Scale function that:
    • Validates desired count is between 1-1000
    • Retrieves current service state via DescribeServices
    • Updates service desired count via UpdateService
    • Returns detailed result with previous and new counts
  • Added ScaleResult type to track operation details
  • Updated cmd/scale.go to use the new Scale function
  • Used lipgloss for consistent bold formatting of cluster/service names

Test Plan

  • Test scaling up a service (e.g., from 2 to 5 tasks)
  • Test scaling down a service (e.g., from 5 to 1 task)
  • Verify validation prevents scaling to 0
  • Verify validation prevents scaling above 1000
  • Test with invalid service names to ensure proper error handling
  • Verify output displays previous and new task counts correctly

Summary by CodeRabbit

  • New Features

    • Introduced a scale command to instantly adjust an ECS service’s task count: runecs scale --service <cluster/service>. Validates count (1–1000), supports cancellation (Ctrl+C), and confirms the previous and new desired counts on success.
  • Documentation

    • Added a “Scale ECS Services” section explaining the new command, usage, and behavior (immediate scaling via UpdateService).

meap added 3 commits August 16, 2025 20:01
Add new scale command to adjust the number of running tasks for a service.
Command accepts a value parameter (desired task count) and requires the
--service flag. Currently implements Hello World output for testing.

- Add scale.go with command structure following existing patterns
- Implement value validation (0-1000 tasks)
- Add structured logging with slog
- Initialize AWS clients for future ECS integration
- Follow Go idiomatic patterns with proper error handling
- Add Scale function in internal/ecs/scale.go to update service desired count
- Add validation to prevent scaling to 0 tasks (minimum is 1)
- Add ScaleResult type to track scaling operation details
- Update scale command to use new Scale function instead of placeholder
- Use lipgloss for bold formatting of cluster/service names in output
- Change minimum task count validation from 0 to 1
Document the new scale feature that uses UpdateService API for direct task count adjustment
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 16, 2025

Walkthrough

Adds a new CLI command to scale ECS services, internal ECS scaling logic with validation and UpdateService call, supporting types for results, and README documentation describing the new command.

Changes

Cohort / File(s) Summary
Documentation
README.md
Adds “Scale ECS Services” section documenting runecs scale usage and behavior.
CLI Command
cmd/scale.go
Introduces scale <value> command; validates input, parses --service, initializes AWS clients, handles signals, calls ecs.Scale, and prints result.
ECS Scaling Logic
internal/ecs/scale.go, internal/ecs/types.go
Implements ecs.Scale with desired count validation, service lookup, and UpdateService call; exports ErrInvalidDesiredCount; adds ScaleResult struct.

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant CLI as runecs scale
  participant ECS as ecs.Scale
  participant AWS as AWS ECS API

  U->>CLI: runecs scale <value> --service <cluster>/<service>
  CLI->>CLI: Validate args, parse flags, setup context
  CLI->>ECS: Scale(ctx, clients, cluster, service, desiredCount)
  ECS->>AWS: DescribeServices(cluster, service)
  AWS-->>ECS: Service details (state, desiredCount)
  ECS->>AWS: UpdateService(DesiredCount)
  AWS-->>ECS: UpdateService response
  ECS-->>CLI: ScaleResult(previous, new, identifiers)
  CLI-->>U: Print scaled from <prev> to <new>
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I twitch my ears at numbers that rise,
Five little tasks, now ten surprise!
With gentle paws I nudge the scale,
Describe, update—no fluff, no fail.
In clouds I hop, services grow—
Carrots count, and pods in tow. 🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.2.2)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/product/migration-guide for migration instructions

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch meap/scale

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@meap meap changed the title ✨ feat: add ECS service scaling command Add ECS service scaling command Aug 16, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (7)
README.md (1)

73-82: Document the valid scaling range (1–1000) and that scaling to 0 is not allowed.

The CLI and ECS layer enforce 1–1000; adding this note will save users a round trip and align expectations.

Apply this diff to add the note:

 ### Scale ECS Services

 The desired count of tasks for an ECS service can be adjusted instantly:

 ```bash
 runecs scale 5 --service mycanvas-ecs-staging-cluster/web

-This command directly modifies the service's desired count using UpdateService, providing immediate scaling without creating task sets or managing deployment configurations.
+This command directly modifies the service's desired count using UpdateService, providing immediate scaling without creating task sets or managing deployment configurations.
+Note: Desired count must be between 1 and 1000; scaling to 0 is not permitted.


</blockquote></details>
<details>
<summary>cmd/scale.go (3)</summary><blockquote>

`43-47`: **Remove redundant arg count check in PreRunE (cobra.ExactArgs already enforces this).**

You already set `Args: cobra.ExactArgs(1)`. The manual `len(args)` check is duplicate.


Apply this diff:

```diff
-func scalePreRunE(cmd *cobra.Command, args []string) error {
-	if len(args) != 1 {
-		return errors.New("scale command requires exactly one argument: the desired task count")
-	}
+func scalePreRunE(cmd *cobra.Command, args []string) error {

48-58: Avoid duplicating validation logic between CLI and ECS layer.

You parse and range-check here and then again in the ECS layer (which also maintains the canonical min/max). Consider either:

  • Keeping only the parse here and delegating range validation to ecs.Scale (preferred to avoid drift), or
  • Importing and reusing shared bounds from ecs, if you want early CLI validation.

Current duplication increases maintenance burden if limits change.

Would you like a follow-up patch to move all range checks into ecs.Scale and report user-friendly messages at the CLI?


92-95: Use Cobra’s output writer for better testability and consistency.

Prefer cmd.Printf over fmt.Printf so output respects command’s configured writer and is easier to capture in tests.

Apply this diff:

-fmt.Printf("Service %s scaled from %d to %d tasks\n",
-	boldStyle.Render(fmt.Sprintf("%s/%s", result.ClusterName, result.ServiceName)),
-	result.PreviousDesiredCount, result.NewDesiredCount)
+cmd.Printf("Service %s scaled from %d to %d tasks\n",
+	boldStyle.Render(fmt.Sprintf("%s/%s", result.ClusterName, result.ServiceName)),
+	result.PreviousDesiredCount, result.NewDesiredCount)
internal/ecs/scale.go (3)

52-56: Guard against DAEMON services (cannot scale desired count).

DAEMON-scheduled services ignore DesiredCount and UpdateService with DesiredCount is invalid. Proactively return a clear error.

Apply this diff (adds a types import and the guard):

 import (
 	"context"
 	"errors"
 	"fmt"
 
 	"github.com/aws/aws-sdk-go-v2/service/ecs"
+	"github.com/aws/aws-sdk-go-v2/service/ecs/types"
 )
@@
 	svc := describeResp.Services[0]
 	if svc.Status == nil || *svc.Status != "ACTIVE" {
 		return nil, fmt.Errorf("service %s is not in ACTIVE state", service)
 	}
 
+	if svc.SchedulingStrategy != nil && *svc.SchedulingStrategy == types.SchedulingStrategyDaemon {
+		return nil, fmt.Errorf("service %s uses DAEMON scheduling and cannot be scaled by desired count", service)
+	}

75-77: Validate UpdateService response structure for consistency with other operations.

Elsewhere (deploy/restart) you error if Service or ServiceArn is missing. Consider doing the same here to keep invariants consistent and catch unexpected API responses.

Apply this diff:

-if updateResp.Service != nil && updateResp.Service.ServiceArn != nil {
-	result.ServiceArn = *updateResp.Service.ServiceArn
-}
+if updateResp.Service == nil {
+	return nil, errors.New("invalid service update response: missing service")
+}
+if updateResp.Service.ServiceArn == nil {
+	return nil, errors.New("invalid service update response: missing service ARN")
+}
+result.ServiceArn = *updateResp.Service.ServiceArn

29-38: Align error semantics for out-of-range desired counts.

ErrInvalidDesiredCount mentions only “greater than 0” but not the upper bound. Either expand its meaning (min/max) or use two sentinel errors (e.g., ErrDesiredCountTooLow, ErrDesiredCountTooHigh). As-is, callers checking for ErrInvalidDesiredCount won’t catch the “too high” case.

If you want, I can draft a small error utility to format consistent messages and expose min/max for reuse in the CLI.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6315b2c and a3641d7.

📒 Files selected for processing (4)
  • README.md (1 hunks)
  • cmd/scale.go (1 hunks)
  • internal/ecs/scale.go (1 hunks)
  • internal/ecs/types.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
internal/ecs/scale.go (3)
internal/ecs/types.go (2)
  • AWSClients (28-32)
  • ScaleResult (136-142)
internal/ecs/restart.go (2)
  • forceNewDeploy (61-91)
  • Restart (93-114)
internal/ecs/deploy.go (1)
  • Deploy (78-103)
🔇 Additional comments (1)
internal/ecs/types.go (1)

135-142: LGTM: ScaleResult is clear and consistent with existing result types.

Field names and doc comment are consistent with other result structs. No issues.

Comment thread internal/ecs/scale.go
Comment on lines +57 to +58
previousDesiredCount := svc.DesiredCount

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential compile error: DesiredCount is a pointer in aws-sdk-go-v2.

svc.DesiredCount is *int32 in ECS v2 models. Assigning it directly to an int32 field will not compile. Safely dereference (and guard nil).

Apply this diff:

-previousDesiredCount := svc.DesiredCount
+var previousDesiredCount int32
+if svc.DesiredCount != nil {
+	previousDesiredCount = *svc.DesiredCount
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
previousDesiredCount := svc.DesiredCount
var previousDesiredCount int32
if svc.DesiredCount != nil {
previousDesiredCount = *svc.DesiredCount
}
🤖 Prompt for AI Agents
In internal/ecs/scale.go around lines 57-58, svc.DesiredCount is a *int32 in
aws-sdk-go-v2 so assigning it directly to an int32 will fail to compile; update
the assignment to safely dereference with a nil guard (e.g., if svc.DesiredCount
!= nil { previousDesiredCount = *svc.DesiredCount } else { previousDesiredCount
= 0 }) or use the SDK helper aws.ToInt32(svc.DesiredCount) to get an int32
value.

@meap meap merged commit d6687a5 into main Aug 16, 2025
1 check passed
@meap meap deleted the meap/scale branch August 16, 2025 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant