Projects STRLCPY cdebug Commits c22e49b4
🤬
  • Fix IO forwarding in non-interactive mode

    And add --privileged flag
  • Loading...
  • Ivan Velichko committed 2 years ago
    c22e49b4
    1 parent 845a3bfe
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■
    README.md
    skipped 89 lines
    90 90   45 root 0:00 ps auxf
    91 91  ```
    92 92   
    93  -## Demo 2: An interactive shell with advanced tools and code editor
     93 +## Demo 2: An interactive shell with code editor
    94 94   
    95 95  If the tools provided by busybox aren't enough, you can bring your own tools with
    96 96  a ~~little~~ huge help of the [nixery](https://nixery.dev/) project:
    97 97   
    98 98  ```sh
    99  -cdebug exec -it --image nixery.dev/shell/ps/findutils/tcpdump/vim my-distroless
     99 +cdebug exec -it --image nixery.dev/shell/vim my-distroless
     100 +```
     101 + 
     102 +## Demo 3: An interactive shell with tshark and other advanced tools
     103 + 
     104 +Even more powerful exammple:
     105 + 
     106 +```sh
     107 +cdebug exec -it --image nixery.dev/shell/ps/findutils/tshark my-distroless
    100 108  ```
    101 109   
    102 110  ## How it works
    skipped 28 lines
    131 139   
    132 140  - Make exec accept (partial) container IDs (only names are supported at the moment)
    133 141  - Terminal resizing ([example](https://github.com/docker/cli/blob/110c4d92b883357c9fb3edc344c4fbec5f77896f/cli/command/container/tty.go#L71))
    134  -- More `exec` flags (like in `docker run`): `--privileged`, `--volume`, `--env`, etc.
     142 +- More `exec` flags (like in `docker run`): `--volume`, `--env`, etc.
    135 143  - Helper command(s) suggesting nix(ery) packages
    136 144  - E2E Tests
    137 145  - Cross-platform builds + goreleaser
    skipped 6 lines
  • ■ ■ ■ ■ ■ ■
    cmd/exec/exec.go
    skipped 3 lines
    4 4   "bytes"
    5 5   "context"
    6 6   "fmt"
    7  - "html/template"
    8 7   "io"
    9 8   "strings"
     9 + "text/template"
    10 10   
    11 11   "github.com/docker/docker/api/types"
    12 12   "github.com/docker/docker/api/types/container"
    13 13   dockerclient "github.com/docker/docker/client"
     14 + "github.com/docker/docker/pkg/stdcopy"
    14 15   "github.com/google/uuid"
    15 16   "github.com/sirupsen/logrus"
    16 17   "github.com/spf13/cobra"
    skipped 14 lines
    31 32  )
    32 33   
    33 34  type options struct {
    34  - target string
    35  - name string
    36  - image string
    37  - tty bool
    38  - stdin bool
    39  - cmd []string
     35 + target string
     36 + name string
     37 + image string
     38 + tty bool
     39 + stdin bool
     40 + cmd []string
     41 + privileged bool
    40 42  }
    41 43   
    42 44  func NewCommand(cli cmd.CLI) *cobra.Command {
    skipped 32 lines
    75 77   "interactive",
    76 78   "i",
    77 79   false,
    78  - "Keep the STDIN open (same as in `docker exec -i`)",
     80 + "Keep the STDIN open (as in `docker exec -i`)",
    79 81   )
    80 82   flags.BoolVarP(
    81 83   &opts.tty,
    82 84   "tty",
    83 85   "t",
    84 86   false,
    85  - "Allocate a pseudo-TTY (same as in `docker exec -t`)",
     87 + "Allocate a pseudo-TTY (as in `docker exec -t`)",
     88 + )
     89 + flags.BoolVar(
     90 + &opts.privileged,
     91 + "privileged",
     92 + false,
     93 + "God mode for the debugger container (as in `docker run --privileged`)",
    86 94   )
    87 95   
    88 96   return cmd
    skipped 5 lines
    94 102   // while chroot-ing and the operation cannot be finished (execve fails
    95 103   // with ENOENT, likely because of the missing/misplaced ELF interpreter).
    96 104   chrootProgramBusybox = template.Must(template.New("busybox-chroot").Parse(`
    97  -set -euo pipefail
     105 +set -eumo pipefail
    98 106   
    99 107  sleep 999999999 &
    100 108  SANDBOX_PID=$!
    skipped 2 lines
    103 111   
    104 112  export PATH=$PATH:/.cdebug-{{ .ID }}
    105 113   
    106  -chroot /proc/1/root sh
     114 +chroot /proc/1/root {{ .Cmd }}
    107 115  `))
    108 116   
    109 117   chrootProgramNixery = template.Must(template.New("nixery-chroot").Parse(`
    110  -set -euxo pipefail
     118 +set -eumo pipefail
    111 119   
    112 120  sleep 999999999 &
    113 121  SANDBOX_PID=$!
    skipped 5 lines
    119 127   
    120 128  export PATH=$PATH:/.cdebug-{{ .ID }}
    121 129   
    122  -chroot /proc/1/root sh
     130 +chroot /proc/1/root {{ .Cmd }}
    123 131  `))
    124 132  )
    125 133   
    skipped 28 lines
    154 162   return chrootProgramNixery
    155 163   }
    156 164   return chrootProgramBusybox
    157  - }(), map[string]string{"ID": runID}),
     165 + }(), map[string]string{
     166 + "ID": runID,
     167 + "Cmd": func() string {
     168 + // TODO: Use `sh -i` when -it is passed.
     169 + if len(opts.cmd) == 0 {
     170 + return "sh"
     171 + }
     172 + return "sh -c '" + strings.Join(shellescape(opts.cmd), " ") + "'"
     173 + }(),
     174 + }),
    158 175   },
    159  - // AttachStdin: true,
     176 + Tty: opts.tty,
    160 177   OpenStdin: opts.stdin,
    161  - Tty: opts.tty,
     178 + // AttachStdin: true,
     179 + AttachStdout: true,
     180 + AttachStderr: true,
    162 181   },
    163 182   &container.HostConfig{
     183 + Privileged: opts.privileged,
    164 184   NetworkMode: container.NetworkMode(target),
    165 185   PidMode: container.PidMode(target),
    166 186   UTSMode: container.UTSMode(target),
    skipped 7 lines
    174 194   return fmt.Errorf("cannot create debugger container: %w", err)
    175 195   }
    176 196   
    177  - if opts.stdin || opts.tty {
    178  - close, err := attachDebugger(ctx, cli, client, opts, resp.ID)
    179  - if err != nil {
    180  - return fmt.Errorf("cannot attach to debugger container: %w", err)
    181  - }
    182  - defer close()
     197 + close, err := attachDebugger(ctx, cli, client, opts, resp.ID)
     198 + if err != nil {
     199 + return fmt.Errorf("cannot attach to debugger container: %w", err)
    183 200   }
     201 + defer close()
    184 202   
    185 203   if err := client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
    186 204   return fmt.Errorf("cannot start debugger container: %w", err)
    skipped 88 lines
    275 293  }
    276 294   
    277 295  func (s *ioStreamer) stream(ctx context.Context) error {
    278  - // TODO: check stdin && tty flags
    279  - s.streams.InputStream().SetRawTerminal()
    280  - s.streams.OutputStream().SetRawTerminal()
    281  - defer func() {
    282  - s.streams.InputStream().RestoreTerminal()
    283  - s.streams.OutputStream().RestoreTerminal()
    284  - }()
     296 + if s.tty {
     297 + s.streams.InputStream().SetRawTerminal()
     298 + s.streams.OutputStream().SetRawTerminal()
     299 + defer func() {
     300 + s.streams.InputStream().RestoreTerminal()
     301 + s.streams.OutputStream().RestoreTerminal()
     302 + }()
     303 + }
    285 304   
    286 305   inDone := make(chan error)
    287 306   go func() {
    288  - io.Copy(s.resp.Conn, s.inputStream)
     307 + if s.stdin {
     308 + if _, err := io.Copy(s.resp.Conn, s.inputStream); err != nil {
     309 + logrus.Debugf("Error forwarding stdin: %s", err)
     310 + }
     311 + }
    289 312   close(inDone)
    290 313   }()
    291 314   
    292 315   outDone := make(chan error)
    293 316   go func() {
    294  - io.Copy(s.outputStream, s.resp.Reader)
     317 + var err error
     318 + if s.tty {
     319 + _, err = io.Copy(s.outputStream, s.resp.Reader)
     320 + } else {
     321 + _, err = stdcopy.StdCopy(s.outputStream, s.errorStream, s.resp.Reader)
     322 + }
     323 + if err != nil {
     324 + logrus.Debugf("Error forwarding stdout/stderr: %s", err)
     325 + }
    295 326   close(outDone)
    296 327   }()
    297 328   
    skipped 29 lines
    327 358   return buf.String()
    328 359  }
    329 360   
     361 +// FIXME: Too naive. This will break for args containing escaped symbols.
     362 +func shellescape(args []string) (escaped []string) {
     363 + for _, a := range args {
     364 + if strings.ContainsAny(a, " \t\n\r") {
     365 + a = `"` + a + `"`
     366 + }
     367 + escaped = append(escaped, a)
     368 + }
     369 + return
     370 +}
     371 + 
Please wait...
Page is in error, reload to recover