Skip to content
This repository has been archived by the owner on Jul 28, 2021. It is now read-only.

Adds console resize support. #84

Merged
merged 5 commits into from Aug 8, 2017
Merged

Adds console resize support. #84

merged 5 commits into from Aug 8, 2017

Conversation

jterry75
Copy link
Contributor

Resolves: #76

@msftclas
Copy link

@jterry75,
Thanks for your contribution as a Microsoft full-time employee or intern. You do not need to sign a CLA.
Thanks,
Microsoft Pull Request Bot

@jterry75
Copy link
Contributor Author

I have verified this through docker for both the init process and an exec process such as VI that the console resize is indeed happening.

@jstarks
Copy link
Member

jstarks commented Jul 31, 2017

What happens if the TTY relay finishes and closes master just as the resize console call is coming down?

It seems like some synchronization might be missing there.

@jterry75
Copy link
Contributor Author

jterry75 commented Aug 1, 2017

I certainly could add synchronization but I didn't for two reasons.

  1. If the resize is right before a close it will succeed. If the resize is right after a close you will get an error from the ioctl call which is no different than returning an error "invalid process" or whatever from locking and detecting the case. IE: the kernel is already a form of synchronization.
  2. What is the case where you have a TTY exit the process and then resize it? The TTY case as far as I know is only for interactive sessions in which I am not physically fast enough to exit the process while resizing it.

All that said I am happy to add a mutex on the relay that exposes the console handle. Let me know if you still want it.

@jstarks
Copy link
Member

jstarks commented Aug 1, 2017

The kernel is not a safe form of synchronization. The fd can get reused right after the close call, so you will send the ioctl to the wrong place.

Why can't the relay implement the resize method? Then you can synchronize internally without exposing a mutex.

@jterry75 jterry75 force-pushed the console_resize branch 4 times, most recently from a57d421 to 7026cdf Compare August 1, 2017 18:12
if !ok {
entry, ok = c.externalProcessCache[pid]
if !ok {
if id == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually expect the HCS to send an empty containerID string for external processes? I didn't know about this behavior, just making sure this is something we can definitely rely on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ContainerID is not a required field. The HCS will not send any parameter and the unmarshal call from json will default "". I could be wrong but this seems safe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@@ -106,7 +115,7 @@ func (c *MockCore) SignalContainer(id string, signal oslayer.Signal) error {
}

// SignalProcess captures its arguments and returns a nil error.
func (c *MockCore) SignalProcess(pid int, options prot.SignalProcessOptions) error {
func (c *MockCore) SignalProcess(id string, pid int, options prot.SignalProcessOptions) error {
c.LastSignalProcess = SignalProcessCall{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add ID field to SignalProcessCall

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And update tests to match.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@@ -159,11 +168,23 @@ func (c *MockCore) RegisterContainerExitHook(id string, exitHook func(oslayer.Pr

// RegisterProcessExitHook captures its arguments, runs the given exit hook on
// a process exit state with exit code 103, and returns a nil error.
func (c *MockCore) RegisterProcessExitHook(pid int, exitHook func(oslayer.ProcessExitState)) error {
func (c *MockCore) RegisterProcessExitHook(id string, pid int, exitHook func(oslayer.ProcessExitState)) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add ID field to RegisterProcessExitHookCall

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And update tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@@ -528,8 +536,8 @@ func (c *container) startProcess(tempProcessDir string, hasTerminal bool, stdioS
}

var relay *stdio.TtyRelay
var master *os.File
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm probably missing something, but why move this to a higher scope?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was a remnant before we used the TtyRelay to do the work. Put back now.


"github.com/Microsoft/opengcs/service/gcs/transport"
"github.com/Sirupsen/logrus"
"github.com/pkg/errors"

"golang.org/x/sys/unix"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: To keep with the pattern we've started following (using goimports to perform formatting), this import should probably go with the above github.com imports.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return errors.New("error resizing console pty is closed")
}

if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, r.pty.Fd(), uintptr(unix.TIOCSWINSZ), uintptr(unsafe.Pointer(&consoleSize{Height: height, Width: width}))); err != 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Syscall [](start = 25, length = 7)

Probably you should make a standalone function in tty.go that calls this IOCTL that can be used or tested separately from the relay.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// small window that ResizeConsole could still get an invalid Fd. We call wait again to
// enusure that no ResizeConsole call came in before actualling closing the pty.
atomic.StoreInt32(&r.closing, 1)
r.wg.Wait()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wg [](start = 3, length = 2)

I think you need to use a separate mutex unfortunately; according to the docs, you can't legally increment the wait group once Wait has been called:

go doc sync.WaitGroup.Add
func (wg *WaitGroup) Add(delta int)
Add adds delta, which may be negative, to the WaitGroup counter. If the
counter becomes zero, all goroutines blocked on Wait are released. If the
counter goes negative, Add panics.

Note that calls with a positive delta that occur when the counter is zero
must happen before a Wait. Calls with a negative delta, or calls with a
positive delta that start when the counter is greater than zero, may happen
at any time. Typically this means the calls to Add should execute before the
statement creating the goroutine or other event to be waited for. If a
WaitGroup is reused to wait for several independent sets of events, new Add
calls must happen after all previous Wait calls have returned. See the
WaitGroup example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


// ResizeConsole sends the appropriate resize to a pTTY FD
// Synchronization of pty should be handled in the callers context.
func ResizeConsole(pty *os.File, height, width uint16) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ResizeConsole [](start = 5, length = 13)

put this in tty.go

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


if pty == nil {
return errors.New("error resizing console for nil pty fd")
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

containerEntry.ExitStatus = state
for _, hook := range containerEntry.ExitHooks {
hook(state)
}
delete(c.containerCache, id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why get rid of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a mistake for sure. Not sure how I missed that

Copy link
Contributor

@beweedon beweedon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I just have two more quick suggestions!

It("should receive the correct values", func() {
Expect(callArgs.Pid).To(Equal(102))
Expect(callArgs.Height).To(Equal(uint16(80)))
Expect(callArgs.Width).To(Equal(uint16(80)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, forgot to mention this on my last review: If we're getting rid of the id=="" check in this PR, we should probably get rid of this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

r.s.Close()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: unnecessary newline

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

// entry.
processCache map[int]*processCacheEntry

externalProcessCacheMutex sync.RWMutex
// externalProcessCache stores information about external processes which
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

externalProcessCache [](start = 4, length = 20)

comment needs updating

@@ -43,6 +43,7 @@ type Process interface {
Wait() (oslayer.ProcessExitState, error)
Pid() int
Delete() error
Tty() *stdio.TtyRelay
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tty [](start = 1, length = 3)

Maybe better to just expose the Resize method directly here? This seems to leak an implementation detail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will open an issue to unify the runtime Process and the oslayer Process interfaces. The issue is that for container/external processes we store them quite differently and the cache cant support both. We really should be thinking about them the same with a single Process interface from the GCS perspective. Will this work for now?

@jstarks
Copy link
Member

jstarks commented Aug 3, 2017

🕐

Removes the per container cache for processes as they are all external pid's
Changes to use a mutex instead of the wait group for ResizeConsole so we are confident of order.
Puts back some of the containerID overloads because HCS always sends this even for external processes.
@jterry75
Copy link
Contributor Author

jterry75 commented Aug 7, 2017

Anymore feedback here? Would like to get this checked in

Copy link
Contributor

@beweedon beweedon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@soccerGB soccerGB merged commit 874b671 into master Aug 8, 2017
@beweedon beweedon deleted the console_resize branch August 8, 2017 00:45
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants