@@ -19,6 +19,7 @@ import (
1919 "github.com/docker/docker/api/types/container"
2020 "github.com/docker/docker/api/types/mount"
2121 "github.com/docker/docker/client"
22+ "github.com/docker/docker/pkg/stdcopy"
2223 "github.com/docker/go-connections/nat"
2324 "github.com/kballard/go-shellquote"
2425 "github.com/leighmcculloch/silo/backend" // parent package
@@ -149,6 +150,15 @@ func (c *Client) Build(ctx context.Context, opts backend.BuildOptions) (string,
149150
150151// Run runs a container with the given options
151152func (c * Client ) Run (ctx context.Context , opts backend.RunOptions ) error {
153+ stdout := opts .Stdout
154+ if stdout == nil {
155+ stdout = io .Discard
156+ }
157+ stderr := opts .Stderr
158+ if stderr == nil {
159+ stderr = io .Discard
160+ }
161+
152162 // Convert mounts
153163 var mounts []mount.Mount
154164 for _ , m := range opts .MountsRO {
@@ -228,16 +238,17 @@ func (c *Client) Run(ctx context.Context, opts backend.RunOptions) error {
228238 }
229239
230240 // Create container configuration
241+ attachStdin := ! opts .NoTTY || opts .Stdin != nil
231242 config := & container.Config {
232243 Image : opts .Image ,
233244 WorkingDir : opts .WorkDir ,
234245 Env : opts .Env ,
235246 Entrypoint : entrypoint ,
236247 Cmd : cmd ,
237- Tty : true ,
238- OpenStdin : true ,
239- StdinOnce : true ,
240- AttachStdin : true ,
248+ Tty : ! opts . NoTTY ,
249+ OpenStdin : attachStdin ,
250+ StdinOnce : attachStdin ,
251+ AttachStdin : attachStdin ,
241252 AttachStdout : true ,
242253 AttachStderr : true ,
243254 ExposedPorts : exposedPorts ,
@@ -263,7 +274,7 @@ func (c *Client) Run(ctx context.Context, opts backend.RunOptions) error {
263274 // Attach to the container
264275 attachResp , err := c .cli .ContainerAttach (ctx , resp .ID , container.AttachOptions {
265276 Stream : true ,
266- Stdin : true ,
277+ Stdin : attachStdin ,
267278 Stdout : true ,
268279 Stderr : true ,
269280 })
@@ -280,20 +291,22 @@ func (c *Client) Run(ctx context.Context, opts backend.RunOptions) error {
280291 return fmt .Errorf ("failed to start container: %w" , err )
281292 }
282293
283- // Set terminal to raw mode and handle resizing
284- fd := os .Stdin .Fd ()
285- if term .IsTerminal (fd ) {
286- oldState , err := term .MakeRaw (fd )
287- if err != nil {
288- return fmt .Errorf ("failed to set raw terminal: %w" , err )
289- }
290- defer term .RestoreTerminal (fd , oldState )
294+ if ! opts .NoTTY {
295+ // Set terminal to raw mode and handle resizing
296+ fd := os .Stdin .Fd ()
297+ if term .IsTerminal (fd ) {
298+ oldState , err := term .MakeRaw (fd )
299+ if err != nil {
300+ return fmt .Errorf ("failed to set raw terminal: %w" , err )
301+ }
302+ defer term .RestoreTerminal (fd , oldState )
291303
292- // Set initial terminal size
293- c .resizeContainerTTY (ctx , resp .ID , fd )
304+ // Set initial terminal size
305+ c .resizeContainerTTY (ctx , resp .ID , fd )
294306
295- // Handle terminal resize signals
296- go c .monitorTTYSize (ctx , resp .ID , fd )
307+ // Handle terminal resize signals
308+ go c .monitorTTYSize (ctx , resp .ID , fd )
309+ }
297310 }
298311
299312 // Forward SIGINT/SIGTERM to stop the container
@@ -308,55 +321,65 @@ func (c *Client) Run(ctx context.Context, opts backend.RunOptions) error {
308321 }
309322 }()
310323
311- // Copy stdin to container, intercepting triple Ctrl-C to kill
312- // Use a context to stop the goroutine when the container exits
313- stdinCtx , stdinCancel := context .WithCancel (ctx )
314- defer stdinCancel ()
315- go func () {
316- var firstCtrlC time.Time
317- var ctrlCCount int
318- buf := make ([]byte , 256 )
319- for {
320- // Check if we should stop
321- select {
322- case <- stdinCtx .Done ():
323- return
324- default :
325- }
324+ if opts .NoTTY {
325+ if opts .Stdin != nil {
326+ go func () {
327+ _ , _ = io .Copy (attachResp .Conn , opts .Stdin )
328+ _ = attachResp .CloseWrite ()
329+ }()
330+ }
331+ _ , _ = stdcopy .StdCopy (stdout , stderr , attachResp .Reader )
332+ } else {
333+ // Copy stdin to container, intercepting triple Ctrl-C to kill
334+ // Use a context to stop the goroutine when the container exits
335+ stdinCtx , stdinCancel := context .WithCancel (ctx )
336+ defer stdinCancel ()
337+ go func () {
338+ var firstCtrlC time.Time
339+ var ctrlCCount int
340+ buf := make ([]byte , 256 )
341+ for {
342+ // Check if we should stop
343+ select {
344+ case <- stdinCtx .Done ():
345+ return
346+ default :
347+ }
326348
327- n , err := os .Stdin .Read (buf )
328- if n > 0 {
329- // Check for Ctrl-C (0x03)
330- for i := 0 ; i < n ; i ++ {
331- if buf [i ] == 0x03 {
332- now := time .Now ()
333- if ctrlCCount > 0 && now .Sub (firstCtrlC ) < time .Second {
334- ctrlCCount ++
335- if ctrlCCount >= 3 {
336- // Triple Ctrl-C - kill container
337- c .cli .ContainerKill (ctx , resp .ID , "SIGKILL" )
338- return
349+ n , err := os .Stdin .Read (buf )
350+ if n > 0 {
351+ // Check for Ctrl-C (0x03)
352+ for i := 0 ; i < n ; i ++ {
353+ if buf [i ] == 0x03 {
354+ now := time .Now ()
355+ if ctrlCCount > 0 && now .Sub (firstCtrlC ) < time .Second {
356+ ctrlCCount ++
357+ if ctrlCCount >= 3 {
358+ // Triple Ctrl-C - kill container
359+ c .cli .ContainerKill (ctx , resp .ID , "SIGKILL" )
360+ return
361+ }
362+ } else {
363+ firstCtrlC = now
364+ ctrlCCount = 1
339365 }
340- } else {
341- firstCtrlC = now
342- ctrlCCount = 1
343366 }
344367 }
368+ attachResp .Conn .Write (buf [:n ])
369+ }
370+ if err != nil {
371+ break
345372 }
346- attachResp .Conn .Write (buf [:n ])
347- }
348- if err != nil {
349- break
350373 }
351- }
352- attachResp .CloseWrite ()
353- }()
374+ attachResp .CloseWrite ()
375+ }()
354376
355- // Copy container output to stdout
356- io .Copy (os . Stdout , attachResp .Reader )
377+ // Copy container output to stdout
378+ _ , _ = io .Copy (stdout , attachResp .Reader )
357379
358- // Container output is done, cancel stdin copying
359- stdinCancel ()
380+ // Container output is done, cancel stdin copying
381+ stdinCancel ()
382+ }
360383
361384 // Wait for the container to finish
362385 select {
0 commit comments