github.com/pdmccormick/importable-docker-buildx@v0.0.0-20240426161518-e47091289030/monitor/commands/attach.go (about)

     1  package commands
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  
     8  	"github.com/docker/buildx/monitor/types"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  type AttachCmd struct {
    13  	m types.Monitor
    14  
    15  	stdout io.WriteCloser
    16  }
    17  
    18  func NewAttachCmd(m types.Monitor, stdout io.WriteCloser) types.Command {
    19  	return &AttachCmd{m, stdout}
    20  }
    21  
    22  func (cm *AttachCmd) Info() types.CommandInfo {
    23  	return types.CommandInfo{
    24  		Name:        "attach",
    25  		HelpMessage: "attach to a buildx server or a process in the container",
    26  		HelpMessageLong: `
    27  Usage:
    28    attach ID
    29  
    30  ID is for a session (visible via list command) or a process (visible via ps command).
    31  If you attached to a process, use Ctrl-a-c for switching the monitor to that process's STDIO.
    32  `,
    33  	}
    34  }
    35  
    36  func (cm *AttachCmd) Exec(ctx context.Context, args []string) error {
    37  	if len(args) < 2 {
    38  		return errors.Errorf("ID of session or process must be passed")
    39  	}
    40  	ref := args[1]
    41  	var id string
    42  
    43  	isProcess, err := isProcessID(ctx, cm.m, ref)
    44  	if err == nil && isProcess {
    45  		cm.m.Attach(ctx, ref)
    46  		id = ref
    47  	}
    48  	if id == "" {
    49  		refs, err := cm.m.List(ctx)
    50  		if err != nil {
    51  			return errors.Errorf("failed to get the list of sessions: %v", err)
    52  		}
    53  		found := false
    54  		for _, s := range refs {
    55  			if s == ref {
    56  				found = true
    57  				break
    58  			}
    59  		}
    60  		if !found {
    61  			return errors.Errorf("unknown ID: %q", ref)
    62  		}
    63  		cm.m.Detach() // Finish existing attach
    64  		cm.m.AttachSession(ref)
    65  	}
    66  	fmt.Fprintf(cm.stdout, "Attached to process %q. Press Ctrl-a-c to switch to the new container\n", id)
    67  	return nil
    68  }
    69  
    70  func isProcessID(ctx context.Context, c types.Monitor, ref string) (bool, error) {
    71  	sid := c.AttachedSessionID()
    72  	if sid == "" {
    73  		return false, errors.Errorf("no attaching session")
    74  	}
    75  	infos, err := c.ListProcesses(ctx, sid)
    76  	if err != nil {
    77  		return false, err
    78  	}
    79  	for _, p := range infos {
    80  		if p.ProcessID == ref {
    81  			return true, nil
    82  		}
    83  	}
    84  	return false, nil
    85  }