Files
Amir Raminfar 6a014d1e76
Deploy VitePress site to Pages / build (push) Has been cancelled
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
Push container / Push branches and PRs (push) Has been cancelled
Test / Typecheck (push) Has been cancelled
Test / JavaScript Tests (push) Has been cancelled
Test / Go Tests (push) Has been cancelled
Test / Go Staticcheck (push) Has been cancelled
Test / Integration Tests (push) Has been cancelled
feat(cloud): accept container name or id in container-scoped tools (#4743)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 18:15:20 -07:00

131 lines
3.3 KiB
Go

package cloud
import (
"context"
"encoding/json"
"fmt"
"github.com/amir20/dozzle/internal/container"
pb "github.com/amir20/dozzle/proto/cloud"
)
type containerActionArgs struct {
ContainerID string `json:"container_id"`
Host string `json:"host_id"`
}
func executeContainerAction(ctx context.Context, name string, argsJSON string, deps ToolDeps) (*pb.CallToolResponse, error) {
var args containerActionArgs
if err := json.Unmarshal([]byte(argsJSON), &args); err != nil {
return nil, fmt.Errorf("failed to parse arguments: %w", err)
}
action, err := resolveAction(name)
if err != nil {
return nil, err
}
hostID, containerID, err := resolveContainerRef(args.ContainerID, args.Host, deps)
if err != nil {
return nil, err
}
cs, err := deps.HostService.FindContainer(hostID, containerID, deps.Labels)
if err != nil {
return nil, fmt.Errorf("container not found: %w", err)
}
if err := cs.Action(ctx, action); err != nil {
return nil, fmt.Errorf("action failed: %w", err)
}
message := fmt.Sprintf("Successfully %s container %s.", pastTense(action), cs.Container.Name)
return &pb.CallToolResponse{
Success: true,
Result: &pb.CallToolResponse_Action{Action: &pb.ActionResult{
Success: true,
ContainerId: cs.Container.ID,
Action: string(action),
Message: message,
}},
}, nil
}
func executeUpdateContainer(ctx context.Context, argsJSON string, deps ToolDeps) (*pb.CallToolResponse, error) {
var args containerActionArgs
if err := json.Unmarshal([]byte(argsJSON), &args); err != nil {
return nil, fmt.Errorf("failed to parse arguments: %w", err)
}
hostID, containerID, err := resolveContainerRef(args.ContainerID, args.Host, deps)
if err != nil {
return nil, err
}
cs, err := deps.HostService.FindContainer(hostID, containerID, deps.Labels)
if err != nil {
return nil, fmt.Errorf("container not found: %w", err)
}
progressCh := make(chan container.UpdateProgress)
var updated bool
var updateErr error
done := make(chan struct{})
go func() {
updated, updateErr = cs.Update(ctx, progressCh)
close(done)
}()
for range progressCh {
}
<-done
if updateErr != nil {
return nil, fmt.Errorf("update failed: %w", updateErr)
}
message := fmt.Sprintf("Successfully updated container %s by pulling the latest image and recreating it.", cs.Container.Name)
if !updated {
message = fmt.Sprintf("Container %s is already running the latest image. No update was needed.", cs.Container.Name)
}
return &pb.CallToolResponse{
Success: true,
Result: &pb.CallToolResponse_Action{Action: &pb.ActionResult{
Success: true,
ContainerId: cs.Container.ID,
Action: "update",
Message: message,
}},
}, nil
}
func pastTense(action container.ContainerAction) string {
switch action {
case container.Start:
return "started"
case container.Stop:
return "stopped"
case container.Restart:
return "restarted"
case container.Remove:
return "removed"
default:
return string(action) + "ed"
}
}
func resolveAction(name string) (container.ContainerAction, error) {
switch name {
case "start_container":
return container.Start, nil
case "stop_container":
return container.Stop, nil
case "restart_container":
return container.Restart, nil
case "remove_container":
return container.Remove, nil
default:
return "", fmt.Errorf("unknown action: %s", name)
}
}