// ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig. func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error { keys := []byte{} var err error if c.DetachKeys != "" { keys, err = term.ToBytes(c.DetachKeys) if err != nil { return fmt.Errorf("Invalid escape keys (%s) provided", c.DetachKeys) } } container, err := daemon.GetContainer(prefixOrName) if err != nil { return err } if container.IsPaused() { err := fmt.Errorf("Container %s is paused. Unpause the container before attach", prefixOrName) return errors.NewRequestConflictError(err) } inStream, outStream, errStream, err := c.GetStreams() if err != nil { return err } defer inStream.Close() if !container.Config.Tty && c.MuxStreams { errStream = stdcopy.NewStdWriter(errStream, stdcopy.Stderr) outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) } var stdin io.ReadCloser var stdout, stderr io.Writer if c.UseStdin { stdin = inStream } if c.UseStdout { stdout = outStream } if c.UseStderr { stderr = errStream } if err := daemon.containerAttach(container, stdin, stdout, stderr, c.Logs, c.Stream, keys); err != nil { fmt.Fprintf(outStream, "Error attaching: %s\n", err) } return nil }
// ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig. func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerAttachConfig) error { container, err := daemon.GetContainer(prefixOrName) if err != nil { return derr.ErrorCodeNoSuchContainer.WithArgs(prefixOrName) } if container.IsPaused() { return derr.ErrorCodePausedContainer.WithArgs(prefixOrName) } inStream, outStream, errStream, err := c.GetStreams() if err != nil { return err } defer inStream.Close() if !container.Config.Tty && c.MuxStreams { errStream = stdcopy.NewStdWriter(errStream, stdcopy.Stderr) outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) } var stdin io.ReadCloser var stdout, stderr io.Writer if c.UseStdin { stdin = inStream } if c.UseStdout { stdout = outStream } if c.UseStderr { stderr = errStream } if err := daemon.containerAttach(container, stdin, stdout, stderr, c.Logs, c.Stream, c.DetachKeys); err != nil { fmt.Fprintf(outStream, "Error attaching: %s\n", err) } return nil }
// ContainerAttach attaches to logs according to the config passed in. See ContainerAttachConfig. func (c *Container) ContainerAttach(name string, ca *backend.ContainerAttachConfig) error { defer trace.End(trace.Begin(name)) // Look up the container name in the metadata cache to get long ID vc := cache.ContainerCache().GetContainer(name) if vc == nil { return NotFoundError(name) } id := vc.ContainerID client := c.containerProxy.Client() handle, err := c.Handle(id, name) if err != nil { return err } bind, err := client.Interaction.InteractionBind(interaction.NewInteractionBindParamsWithContext(ctx). WithConfig(&models.InteractionBindConfig{ Handle: handle, })) if err != nil { return InternalServerError(err.Error()) } handle, ok := bind.Payload.Handle.(string) if !ok { return InternalServerError(fmt.Sprintf("Type assertion failed for %#+v", handle)) } // commit the handle; this will reconfigure the vm _, err = client.Containers.Commit(containers.NewCommitParamsWithContext(ctx).WithHandle(handle)) if err != nil { switch err := err.(type) { case *containers.CommitNotFound: return NotFoundError(name) case *containers.CommitConflict: return ConflictError(err.Error()) case *containers.CommitDefault: return InternalServerError(err.Payload.Message) default: return InternalServerError(err.Error()) } } clStdin, clStdout, clStderr, err := ca.GetStreams() if err != nil { return InternalServerError("Unable to get stdio streams for calling client") } defer clStdin.Close() if !vc.Config.Tty && ca.MuxStreams { // replace the stdout/stderr with Docker's multiplex stream if ca.UseStdout { clStderr = stdcopy.NewStdWriter(clStderr, stdcopy.Stderr) } if ca.UseStderr { clStdout = stdcopy.NewStdWriter(clStdout, stdcopy.Stdout) } } err = c.containerProxy.AttachStreams(context.Background(), vc, clStdin, clStdout, clStderr, ca) if err != nil { if _, ok := err.(DetachError); ok { log.Infof("Detach detected, tearing down connection") client = c.containerProxy.Client() handle, err = c.Handle(id, name) if err != nil { return err } unbind, err := client.Interaction.InteractionUnbind(interaction.NewInteractionUnbindParamsWithContext(ctx). WithConfig(&models.InteractionUnbindConfig{ Handle: handle, })) if err != nil { return InternalServerError(err.Error()) } handle, ok = unbind.Payload.Handle.(string) if !ok { return InternalServerError("type assertion failed") } // commit the handle; this will reconfigure the vm _, err = client.Containers.Commit(containers.NewCommitParamsWithContext(ctx).WithHandle(handle)) if err != nil { switch err := err.(type) { case *containers.CommitNotFound: return NotFoundError(name) case *containers.CommitDefault: return InternalServerError(err.Payload.Message) default: return InternalServerError(err.Error()) } } } return err } return nil }