Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 13 additions & 9 deletions frontend/src/libcfcontainer/cuttlefish_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package libcfcontainer

import (
"bytes"
"context"
"fmt"
"io"
Expand All @@ -27,6 +28,7 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
)

type CuttlefishContainerManager interface {
Expand All @@ -37,7 +39,7 @@ type CuttlefishContainerManager interface {
// Create adn start a container instance
CreateAndStartContainer(ctx context.Context, additionalConfig *container.Config, additionalHostConfig *container.HostConfig, name string) (string, error)
// Execute a command on a running container instance
ExecOnContainer(ctx context.Context, ctr string, cmd []string) error
ExecOnContainer(ctx context.Context, ctr string, cmd []string) (string, error)
}

type CuttlefishContainerManagerOpts struct {
Expand Down Expand Up @@ -147,21 +149,21 @@ func (m *CuttlefishContainerManagerImpl) CreateAndStartContainer(ctx context.Con
return createRes.ID, nil
}

func (m *CuttlefishContainerManagerImpl) ExecOnContainer(ctx context.Context, ctr string, cmd []string) error {
func (m *CuttlefishContainerManagerImpl) ExecOnContainer(ctx context.Context, ctr string, cmd []string) (string, error) {
execConfig := container.ExecOptions{
AttachStderr: true,
AttachStdin: true,
AttachStdout: true,
Cmd: cmd,
Tty: true,
Tty: false,
}
createRes, err := m.cli.ContainerExecCreate(ctx, ctr, execConfig)
if err != nil {
return fmt.Errorf("failed to create container execution %q: %w", strings.Join(cmd, " "), err)
return "", fmt.Errorf("failed to create container execution %q: %w", strings.Join(cmd, " "), err)
}
attachRes, err := m.cli.ContainerExecAttach(ctx, createRes.ID, container.ExecStartOptions{})
if err != nil {
return fmt.Errorf("failed to attach container execution %q: %w", strings.Join(cmd, " "), err)
return "", fmt.Errorf("failed to attach container execution %q: %w", strings.Join(cmd, " "), err)
}
waitCh := make(chan struct{})
go func() {
Expand All @@ -170,20 +172,22 @@ func (m *CuttlefishContainerManagerImpl) ExecOnContainer(ctx context.Context, ct
log.Printf("failed to propagate standard input: %v", err)
}
}()
var stdoutBuf bytes.Buffer
go func() {
defer close(waitCh)
if _, err := io.Copy(os.Stdout, attachRes.Reader); err != nil {
stdout := io.MultiWriter(os.Stdout, &stdoutBuf)
if _, err := stdcopy.StdCopy(stdout, os.Stderr, attachRes.Reader); err != nil {
log.Printf("failed to propagate standard output: %v", err)
}
}()
<-waitCh
attachRes.Close()
if result, err := m.cli.ContainerExecInspect(ctx, createRes.ID); err != nil {
return fmt.Errorf("failed to run command on the container: %w", err)
return "", fmt.Errorf("failed to run command on the container: %w", err)
} else if result.ExitCode != 0 {
return fmt.Errorf("failed to run command on the container with exit code %d", result.ExitCode)
return "", fmt.Errorf("failed to run command on the container with exit code %d", result.ExitCode)
}
return nil
return stdoutBuf.String(), nil
}

func RootlessPodmanSocketAddr() string {
Expand Down
47 changes: 46 additions & 1 deletion frontend/src/podcvd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ package main
import (
"context"
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"time"

"github.com/google/android-cuttlefish/frontend/src/libcfcontainer"
Expand Down Expand Up @@ -102,6 +104,40 @@ func prepareCuttlefishHost(ccm libcfcontainer.CuttlefishContainerManager) (strin
return id, nil
}

func parseAdbPorts(stdout string) ([]int, error) {
type Instance struct {
AdbPort int `json:"adb_port"`
}
type InstanceGroup struct {
Instances []Instance `json:"instances"`
}
var instanceGroup InstanceGroup
if err := json.Unmarshal([]byte(stdout), &instanceGroup); err != nil {
return nil, err
}
var ports []int
for _, instance := range instanceGroup.Instances {
ports = append(ports, instance.AdbPort)
}
return ports, nil
}

func establishAdbConnection(ports ...int) error {
adbBin, err := exec.LookPath("adb")
if err != nil {
return fmt.Errorf("failed to find adb: %w", err)
}
if err := exec.Command(adbBin, "start-server").Run(); err != nil {
return fmt.Errorf("failed to start server: %w", err)
}
for _, port := range ports {
if err := exec.Command(adbBin, "connect", fmt.Sprintf("localhost:%d", port)).Run(); err != nil {
return fmt.Errorf("failed to connect to Cuttlefish device: %w", err)
}
}
return nil
}

func main() {
// Parse selector and driver options before the subcommand argument only.
// TODO(seungjaeyoo): Handle selector/driver options properly for
Expand Down Expand Up @@ -135,7 +171,16 @@ func main() {
if err != nil {
log.Fatal(err)
}
if err := ccm.ExecOnContainer(context.Background(), id, append([]string{"cvd"}, os.Args[1:]...)); err != nil {
stdout, err := ccm.ExecOnContainer(context.Background(), id, append([]string{"cvd"}, os.Args[1:]...))
if err != nil {
log.Fatal(err)
}
// TODO(seungjaeyoo): Establish ADB connection only when it's required.
ports, err := parseAdbPorts(stdout)
if err != nil {
log.Fatal(err)
}
if err := establishAdbConnection(ports...); err != nil {
log.Fatal(err)
}
}
Loading