Improve terminal reuse and attach

Currently attach and the editor do not share the same logic for saving
and restoring the terminal, and are not suitable for nesting (when the
caller wants to create something, attach, and then delete something when
the attach is over).  This commit moves the interrupt protection logic
to a util package and supports nesting interrupt handlers.
This commit is contained in:
Clayton Coleman
2016-02-20 13:53:11 -05:00
parent 4d98abf26c
commit 3a29e3971b
7 changed files with 242 additions and 106 deletions

View File

@@ -23,13 +23,13 @@ import (
"math/rand"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"strings"
"github.com/docker/docker/pkg/term"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/util/term"
)
const (
@@ -125,7 +125,7 @@ func (e Editor) Launch(path string) error {
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
glog.V(5).Infof("Opening file with editor %v", args)
if err := withSafeTTYAndInterrupts(cmd.Run); err != nil {
if err := (term.TTY{In: os.Stdin, TryDev: true}).Safe(cmd.Run); err != nil {
if err, ok := err.(*exec.Error); ok {
if err.Err == exec.ErrNotFound {
return fmt.Errorf("unable to launch the editor %q", strings.Join(e.Args, " "))
@@ -160,40 +160,6 @@ func (e Editor) LaunchTempFile(prefix, suffix string, r io.Reader) ([]byte, stri
return bytes, path, err
}
// withSafeTTYAndInterrupts invokes the provided function after the terminal
// state has been stored, and then on any error or termination attempts to
// restore the terminal state to its prior behavior. It also eats signals
// for the duration of the function.
func withSafeTTYAndInterrupts(fn func() error) error {
ch := make(chan os.Signal, 1)
signal.Notify(ch, childSignals...)
defer signal.Stop(ch)
inFd := os.Stdin.Fd()
if !term.IsTerminal(inFd) {
if f, err := os.Open("/dev/tty"); err == nil {
defer f.Close()
inFd = f.Fd()
}
}
if term.IsTerminal(inFd) {
state, err := term.SaveState(inFd)
if err != nil {
return err
}
go func() {
if _, ok := <-ch; !ok {
return
}
term.RestoreTerminal(inFd, state)
}()
defer term.RestoreTerminal(inFd, state)
return fn()
}
return fn()
}
func tempFile(prefix, suffix string) (f *os.File, err error) {
dir := os.TempDir()

View File

@@ -1,27 +0,0 @@
// +build !windows
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package editor
import (
"os"
"syscall"
)
// childSignals are the allowed signals that can be sent to children in Unix variant OS's
var childSignals = []os.Signal{syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT}

View File

@@ -1,26 +0,0 @@
// +build windows
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package editor
import (
"os"
)
// childSignals are the allowed signals that can be sent to children in Windows to terminate
var childSignals = []os.Signal{os.Interrupt}