Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
57b9bb7
Support resizing services
syvb Nov 20, 2025
1470748
rm unneeded wait
syvb Nov 20, 2025
24a96ad
rm nodes stuff that doesn't work
syvb Nov 20, 2025
dcc6f22
rm shared stuff
syvb Nov 24, 2025
1ae436c
Revert "rm unneeded wait"
syvb Dec 4, 2025
7142d38
fix import
syvb Dec 11, 2025
4290db6
fix merge issues
syvb Dec 11, 2025
72c194a
Update internal/tiger/cmd/service.go
syvb Dec 15, 2025
d4a7009
group input/output structs
syvb Dec 15, 2025
ff3a7a8
review fixes
syvb Dec 15, 2025
78a12fa
Update openapi.yaml spec and corresponding generated API types
nathanjcochran Jan 8, 2026
36c6bb5
Fix code to account for updated types
nathanjcochran Jan 8, 2026
16a8571
Merge branch 'main' into sv/resize
nathanjcochran Jan 8, 2026
f989e82
Update re: latest changes in main
nathanjcochran Jan 8, 2026
1837c91
Improve CPU memory validation/normalization function, fix broken tests
nathanjcochran Jan 8, 2026
6fb1109
Improve wait logic to do an initial check
nathanjcochran Jan 9, 2026
7449728
Improve wait logic
nathanjcochran Jan 9, 2026
63ef38d
Minor code cleanup and unification
nathanjcochran Jan 9, 2026
a3ed5e4
Clean up and unify MCP tool handler code
nathanjcochran Jan 9, 2026
be70c59
Only return status and resources from service_resize MCP tool call, t…
nathanjcochran Jan 9, 2026
849ce72
Update command description
nathanjcochran Jan 9, 2026
08eeb16
Add integration tests for resizing a service
nathanjcochran Jan 9, 2026
d4120c4
Update documentation
nathanjcochran Jan 12, 2026
a3f90dd
Status code update
nathanjcochran Jan 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ Tiger CLI is a Go-based command-line interface for managing Tiger, the modern da
- **Command Structure**: `internal/tiger/cmd/` - Cobra-based command definitions
- `root.go` - Root command with global flags and configuration initialization
- `auth.go` - Authentication commands (login, logout, status)
- `service.go` - Service management commands (list, create, get, fork, start, stop, delete, update-password)
- `service.go` - Service management commands (list, create, get, fork, start, stop, resize, delete, update-password)
- `db.go` - Database operation commands (connection-string, connect, test-connection)
- `config.go` - Configuration management commands (show, set, unset, reset)
- `mcp.go` - MCP server commands (install, start, list)
Expand All @@ -277,7 +277,7 @@ Tiger CLI is a Go-based command-line interface for managing Tiger, the modern da
- **API Client**: `internal/tiger/api/` - Generated OpenAPI client with mocks
- **MCP Server**: `internal/tiger/mcp/` - Model Context Protocol server implementation
- `server.go` - MCP server initialization, tool registration, and lifecycle management
- `service_tools.go` - Service management tools (list, get, create, fork, start, stop, update-password)
- `service_tools.go` - Service management tools (list, get, create, fork, start, stop, resize, update-password)
- `db_tools.go` - Database operation tools (execute-query)
- `proxy.go` - Proxy client that forwards tools/resources/prompts from remote docs MCP server
- `capabilities.go` - Lists all available MCP capabilities (tools, prompts, resources, resource templates)
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Tiger CLI provides the following commands:
- `fork` - Fork an existing service
- `start` - Start a stopped service
- `stop` - Stop a running service
- `resize` - Resize service CPU and memory allocation
- `delete` - Delete a service
- `update-password` - Update service master password
- `tiger db` - Database operations
Expand Down Expand Up @@ -178,6 +179,7 @@ The MCP server exposes the following tools to AI assistants:
- `service_fork` - Fork an existing database service to create an independent copy
- `service_start` - Start a stopped database service
- `service_stop` - Stop a running database service
- `service_resize` - Resize a database service by changing CPU and memory allocation
- `service_update_password` - Update the master password for a service

**Database Operations:**
Expand Down
8 changes: 8 additions & 0 deletions internal/tiger/api/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 3 additions & 6 deletions internal/tiger/api/types.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion internal/tiger/cmd/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/base64"
"errors"
"fmt"
"net/http"
"os"
"os/exec"
"strings"
Expand Down Expand Up @@ -787,7 +788,7 @@ func getServiceDetails(cmd *cobra.Command, cfg *common.Config, args []string) (a
}

// Handle API response
if resp.StatusCode() != 200 {
if resp.StatusCode() != http.StatusOK {
return api.Service{}, common.ExitWithErrorFromStatusCode(resp.StatusCode(), resp.JSON4XX)
}

Expand Down
145 changes: 145 additions & 0 deletions internal/tiger/cmd/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,146 @@ func TestServiceLifecycleIntegration(t *testing.T) {
}
})

t.Run("ResizeService", func(t *testing.T) {
if serviceID == "" {
t.Skip("No service ID available from create test")
}

t.Logf("Resizing service: %s", serviceID)

// First, get current service details to see current CPU/memory
output, err := executeIntegrationCommand(
t.Context(),
"service", "describe", serviceID,
"--output", "json",
)

if err != nil {
t.Fatalf("Failed to describe service before resize: %v\nOutput: %s", err, output)
}

// Parse JSON to check current resources
var serviceBefore api.Service
if err := json.Unmarshal([]byte(output), &serviceBefore); err != nil {
t.Fatalf("Failed to parse service JSON: %v", err)
}

var currentCPU, currentMemory string
if serviceBefore.Resources != nil && len(*serviceBefore.Resources) > 0 {
resource := (*serviceBefore.Resources)[0]
if resource.Spec != nil {
if resource.Spec.CpuMillis != nil {
cpuCores := float64(*resource.Spec.CpuMillis) / 1000
currentCPU = fmt.Sprintf("%.1f CPU", cpuCores)
}
if resource.Spec.MemoryGbs != nil {
currentMemory = fmt.Sprintf("%d GB", *resource.Spec.MemoryGbs)
}
}
}

t.Logf("Current resources: CPU=%s, Memory=%s", currentCPU, currentMemory)

// Resize to 1 CPU / 4 GB (larger than default 0.5 CPU / 2 GB)
// Note: --cpu expects millicores (1000 = 1 CPU), --memory expects GB as integer
targetCPUMillis := "1000" // 1 CPU = 1000 millicores
targetMemoryGB := "4" // 4 GB

t.Logf("Resizing to: CPU=%s millicores (1 CPU), Memory=%s GB", targetCPUMillis, targetMemoryGB)

output, err = executeIntegrationCommand(
t.Context(),
"service", "resize", serviceID,
"--cpu", targetCPUMillis,
"--memory", targetMemoryGB,
"--wait-timeout", "10m", // Longer timeout for resize operations
)

if err != nil {
t.Fatalf("Service resize failed: %v\nOutput: %s", err, output)
}

// Verify resize success message
if !strings.Contains(output, "Resize completed successfully") &&
!strings.Contains(output, "resized successfully") {
t.Logf("Note: Expected resize success message, got: %s", output)
}

t.Logf("Service resize command completed successfully")
})

t.Run("VerifyServiceResized", func(t *testing.T) {
if serviceID == "" {
t.Skip("No service ID available from create test")
}

t.Logf("Verifying service has been resized")

output, err := executeIntegrationCommand(
t.Context(),
"service", "describe", serviceID,
"--output", "json",
)

if err != nil {
t.Fatalf("Failed to describe service after resize: %v\nOutput: %s", err, output)
}

// Parse JSON to check new resources
var serviceAfter api.Service
if err := json.Unmarshal([]byte(output), &serviceAfter); err != nil {
t.Fatalf("Failed to parse service JSON: %v", err)
}

var newCPUMillis, newMemoryGbs int
if serviceAfter.Resources != nil && len(*serviceAfter.Resources) > 0 {
resource := (*serviceAfter.Resources)[0]
if resource.Spec != nil {
if resource.Spec.CpuMillis != nil {
newCPUMillis = *resource.Spec.CpuMillis
}
if resource.Spec.MemoryGbs != nil {
newMemoryGbs = *resource.Spec.MemoryGbs
}
}
}

newCPU := fmt.Sprintf("%.1f CPU", float64(newCPUMillis)/1000)
newMemory := fmt.Sprintf("%d GB", newMemoryGbs)

t.Logf("New resources after resize: CPU=%s (millis=%d), Memory=%s", newCPU, newCPUMillis, newMemory)

// Verify the service has been resized to expected values
expectedCPUMillis := 1000 // 1 CPU = 1000 millicores
expectedMemoryGbs := 4 // 4 GB

if newCPUMillis != expectedCPUMillis {
t.Errorf("Expected CPU to be %d millicores after resize, got %d", expectedCPUMillis, newCPUMillis)
} else {
t.Logf("✅ CPU correctly resized to %d millicores (1 CPU)", newCPUMillis)
}

if newMemoryGbs != expectedMemoryGbs {
t.Errorf("Expected Memory to be %d GB after resize, got %d", expectedMemoryGbs, newMemoryGbs)
} else {
t.Logf("✅ Memory correctly resized to %d GB", newMemoryGbs)
}

// Verify service is still in READY state after resize
var status string
if serviceAfter.Status != nil {
status = string(*serviceAfter.Status)
}

if status != "READY" {
t.Logf("Warning: Expected service status to be READY after resize, got %s", status)
} else {
t.Logf("✅ Service is correctly in READY state after resize")
}

t.Logf("✅ Service resize verified successfully")
})

t.Run("DeleteService", func(t *testing.T) {
if serviceID == "" {
t.Skip("No service ID available for deletion")
Expand Down Expand Up @@ -1572,6 +1712,11 @@ func TestServiceForkIntegration(t *testing.T) {
t.Fatalf("Login failed: %v\nOutput: %s", err, output)
}

// Verify login success message
if !strings.Contains(output, "Successfully logged in") && !strings.Contains(output, "Logged in") {
t.Errorf("Login output: %s", output)
}

t.Logf("Login successful")
})

Expand Down
Loading
Loading