feat: Allow container matching by ID prefix (#175)

This commit is contained in:
Anton Ovchinnikov
2025-11-10 08:38:20 +10:00
committed by GitHub
parent 2f5f515696
commit 15d4ef5518
5 changed files with 26 additions and 16 deletions
+3 -3
View File
@@ -28,7 +28,7 @@ func NewExecCommand() *cobra.Command {
Use: "exec [OPTIONS] SERVICE [COMMAND ARGS...]", Use: "exec [OPTIONS] SERVICE [COMMAND ARGS...]",
Short: "Execute a command in a running service container", Short: "Execute a command in a running service container",
Long: `Execute a command (interactive shell by default) in a running container within a service. Long: `Execute a command (interactive shell by default) in a running container within a service.
If the service has multiple replicas, the command will be executed in a random container. If the service has multiple replicas and no container ID is specified, the command will be executed in a random container.
`, `,
Example: ` Example: `
# Start an interactive shell ("bash" or "sh" will be tried by default) # Start an interactive shell ("bash" or "sh" will be tried by default)
@@ -37,8 +37,8 @@ If the service has multiple replicas, the command will be executed in a random c
# Start an interactive shell with explicit command # Start an interactive shell with explicit command
uc exec web-service /bin/zsh uc exec web-service /bin/zsh
# List files in the specific container of the service # List files in the specific container of the service; --container accepts full ID or a (unique) prefix
uc exec --container d792ea7347e5 web-service ls -la uc exec --container d792e web-service ls -la
# Pipe input to a command inside the service container # Pipe input to a command inside the service container
cat backup.sql | uc exec -T db-service psql -U postgres mydb cat backup.sql | uc exec -T db-service psql -U postgres mydb
+14 -5
View File
@@ -10,7 +10,6 @@ import (
"github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose/v2/pkg/progress"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/stringid"
"github.com/psviderski/uncloud/internal/docker" "github.com/psviderski/uncloud/internal/docker"
machinedocker "github.com/psviderski/uncloud/internal/machine/docker" machinedocker "github.com/psviderski/uncloud/internal/machine/docker"
"github.com/psviderski/uncloud/internal/secret" "github.com/psviderski/uncloud/internal/secret"
@@ -200,7 +199,7 @@ func toPullProgressEvent(jm jsonmessage.JSONMessage) *progress.Event {
} }
// InspectContainer returns the information about the specified container within the service. // InspectContainer returns the information about the specified container within the service.
// containerNameOrID can be name, ID, or truncated ID of the container. // containerNameOrID can be name, full ID, or ID prefix of the container.
func (cli *Client) InspectContainer( func (cli *Client) InspectContainer(
ctx context.Context, serviceNameOrID, containerNameOrID string, ctx context.Context, serviceNameOrID, containerNameOrID string,
) (api.MachineServiceContainer, error) { ) (api.MachineServiceContainer, error) {
@@ -209,13 +208,23 @@ func (cli *Client) InspectContainer(
return api.MachineServiceContainer{}, fmt.Errorf("inspect service: %w", err) return api.MachineServiceContainer{}, fmt.Errorf("inspect service: %w", err)
} }
// TODO: support matching by any prefix of the container ID prefixMatchCandidates := []api.MachineServiceContainer{}
for _, c := range svc.Containers { for _, c := range svc.Containers {
if c.Container.ID == containerNameOrID || if c.Container.ID == containerNameOrID ||
c.Container.Name == containerNameOrID || c.Container.Name == containerNameOrID {
stringid.TruncateID(c.Container.ID) == containerNameOrID {
return c, nil return c, nil
} }
if strings.HasPrefix(c.Container.ID, containerNameOrID) {
prefixMatchCandidates = append(prefixMatchCandidates, c)
}
}
if len(prefixMatchCandidates) == 1 {
return prefixMatchCandidates[0], nil
} else if len(prefixMatchCandidates) > 1 {
return api.MachineServiceContainer{}, fmt.Errorf(
"multiple containers found with ID prefix '%s'", containerNameOrID)
} }
return api.MachineServiceContainer{}, api.ErrNotFound return api.MachineServiceContainer{}, api.ErrNotFound
+3 -2
View File
@@ -224,7 +224,8 @@ func TestExecBasicCommands(t *testing.T) {
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
containerID := service.Containers[1].Container.ID // Use container prefix
containerIDPrefix := service.Containers[1].Container.ID[:3]
execOptions := api.ExecOptions{ execOptions := api.ExecOptions{
Command: []string{"hostname"}, Command: []string{"hostname"},
AttachStdout: true, AttachStdout: true,
@@ -233,7 +234,7 @@ func TestExecBasicCommands(t *testing.T) {
Stderr: &stderr, Stderr: &stderr,
} }
exitCode, err := cli.ExecContainer(ctx, multiServiceName, containerID, execOptions) exitCode, err := cli.ExecContainer(ctx, multiServiceName, containerIDPrefix, execOptions)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, 0, exitCode) assert.Equal(t, 0, exitCode)
+3 -3
View File
@@ -5,7 +5,7 @@ Execute a command in a running service container
## Synopsis ## Synopsis
Execute a command (interactive shell by default) in a running container within a service. Execute a command (interactive shell by default) in a running container within a service.
If the service has multiple replicas, the command will be executed in a random container. If the service has multiple replicas and no container ID is specified, the command will be executed in a random container.
``` ```
@@ -22,8 +22,8 @@ uc exec [OPTIONS] SERVICE [COMMAND ARGS...] [flags]
# Start an interactive shell with explicit command # Start an interactive shell with explicit command
uc exec web-service /bin/zsh uc exec web-service /bin/zsh
# List files in the specific container of the service # List files in the specific container of the service; --container accepts full ID or a (unique) prefix
uc exec --container d792ea7347e5 web-service ls -la uc exec --container d792e web-service ls -la
# Pipe input to a command inside the service container # Pipe input to a command inside the service container
cat backup.sql | uc exec -T db-service psql -U postgres mydb cat backup.sql | uc exec -T db-service psql -U postgres mydb
@@ -5,7 +5,7 @@ Execute a command in a running service container
## Synopsis ## Synopsis
Execute a command (interactive shell by default) in a running container within a service. Execute a command (interactive shell by default) in a running container within a service.
If the service has multiple replicas, the command will be executed in a random container. If the service has multiple replicas and no container ID is specified, the command will be executed in a random container.
``` ```
@@ -22,8 +22,8 @@ uc service exec [OPTIONS] SERVICE [COMMAND ARGS...] [flags]
# Start an interactive shell with explicit command # Start an interactive shell with explicit command
uc exec web-service /bin/zsh uc exec web-service /bin/zsh
# List files in the specific container of the service # List files in the specific container of the service; --container accepts full ID or a (unique) prefix
uc exec --container d792ea7347e5 web-service ls -la uc exec --container d792e web-service ls -la
# Pipe input to a command inside the service container # Pipe input to a command inside the service container
cat backup.sql | uc exec -T db-service psql -U postgres mydb cat backup.sql | uc exec -T db-service psql -U postgres mydb