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

Commit

Permalink
Adds console resize support.
Browse files Browse the repository at this point in the history
Resolves: #76
  • Loading branch information
jterry75 committed Aug 4, 2017
1 parent 285d650 commit 7556251
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 55 deletions.
10 changes: 5 additions & 5 deletions service/gcs/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func (b *bridge) signalProcess(message []byte) (*prot.MessageResponseBase, error
}
response.ActivityID = request.ActivityID

if err := b.coreint.SignalProcess(int(request.ProcessID), request.Options); err != nil {
if err := b.coreint.SignalProcess(request.ContainerID, int(request.ProcessID), request.Options); err != nil {
return response, err
}

Expand Down Expand Up @@ -348,15 +348,13 @@ func (b *bridge) waitOnProcess(message []byte, header *prot.MessageHeader) (*pro
logrus.Error(errors.Wrapf(err, "failed to send process exit response \"%v\"", response))
}
}
if err := b.coreint.RegisterProcessExitHook(int(request.ProcessID), exitHook); err != nil {
if err := b.coreint.RegisterProcessExitHook(request.ContainerID, int(request.ProcessID), exitHook); err != nil {
return response, err
}

return response, nil
}

// resizeConsole is currently a nop until the functionality is implemented.
// TODO: Tests still need to be written when it's no longer a nop.
func (b *bridge) resizeConsole(message []byte) (*prot.MessageResponseBase, error) {
response := newResponseBase()
var request prot.ContainerResizeConsole
Expand All @@ -365,7 +363,9 @@ func (b *bridge) resizeConsole(message []byte) (*prot.MessageResponseBase, error
}
response.ActivityID = request.ActivityID

// NOP
if err := b.coreint.ResizeConsole(request.ContainerID, int(request.ProcessID), request.Height, request.Width); err != nil {
return response, err
}

return response, nil
}
Expand Down
5 changes: 3 additions & 2 deletions service/gcs/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ type Core interface {
CreateContainer(id string, info prot.VMHostedContainerSettings) error
ExecProcess(id string, info prot.ProcessParameters, stdioSet *stdio.ConnectionSet) (pid int, err error)
SignalContainer(id string, signal oslayer.Signal) error
SignalProcess(pid int, options prot.SignalProcessOptions) error
SignalProcess(id string, pid int, options prot.SignalProcessOptions) error
ListProcesses(id string) ([]runtime.ContainerProcessState, error)
RunExternalProcess(info prot.ProcessParameters, stdioSet *stdio.ConnectionSet) (pid int, err error)
ModifySettings(id string, request prot.ResourceModificationRequestResponse) error
RegisterContainerExitHook(id string, onExit func(oslayer.ProcessExitState)) error
RegisterProcessExitHook(pid int, onExit func(oslayer.ProcessExitState)) error
RegisterProcessExitHook(id string, pid int, onExit func(oslayer.ProcessExitState)) error
ResizeConsole(id string, pid int, height, width uint16) error
}
142 changes: 99 additions & 43 deletions service/gcs/core/gcs/gcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,6 @@ type gcsCore struct {
// ID to cache entry.
containerCache map[string]*containerCacheEntry

processCacheMutex sync.RWMutex
// processCache stores information about processes which persists between
// calls into the gcsCore. It is structured as a map from pid to cache
// entry.
processCache map[int]*processCacheEntry

externalProcessCacheMutex sync.RWMutex
// externalProcessCache stores information about external processes which
// persists between calls into the gcsCore. It is structured as a map from
Expand All @@ -56,7 +50,6 @@ func NewGCSCore(rtime runtime.Runtime, os oslayer.OS) *gcsCore {
Rtime: rtime,
OS: os,
containerCache: make(map[string]*containerCacheEntry),
processCache: make(map[int]*processCacheEntry),
externalProcessCache: make(map[int]*processCacheEntry),
}
}
Expand All @@ -65,27 +58,30 @@ func NewGCSCore(rtime runtime.Runtime, os oslayer.OS) *gcsCore {
type containerCacheEntry struct {
ID string
ExitStatus oslayer.ProcessExitState
Processes []int
ExitHooks []func(oslayer.ProcessExitState)
MappedVirtualDisks map[uint8]prot.MappedVirtualDisk
MappedDirectories map[uint32]prot.MappedDirectory
NetworkAdapters []prot.NetworkAdapter
container runtime.Container

processCacheMutex sync.RWMutex
// processCache stores information about processes which persists between
// calls into the gcsCore. It is structured as a map from pid to cache
// entry.
processCache map[int]*processCacheEntry
}

func newContainerCacheEntry(id string) *containerCacheEntry {
return &containerCacheEntry{
ID: id,
MappedVirtualDisks: make(map[uint8]prot.MappedVirtualDisk),
MappedDirectories: make(map[uint32]prot.MappedDirectory),
processCache: make(map[int]*processCacheEntry),
}
}
func (e *containerCacheEntry) AddExitHook(hook func(oslayer.ProcessExitState)) {
e.ExitHooks = append(e.ExitHooks, hook)
}
func (e *containerCacheEntry) AddProcess(pid int) {
e.Processes = append(e.Processes, pid)
}
func (e *containerCacheEntry) AddNetworkAdapter(adapter prot.NetworkAdapter) {
e.NetworkAdapters = append(e.NetworkAdapters, adapter)
}
Expand Down Expand Up @@ -122,6 +118,7 @@ func (e *containerCacheEntry) RemoveMappedDirectory(dir prot.MappedDirectory) {
type processCacheEntry struct {
ExitStatus oslayer.ProcessExitState
ExitHooks []func(oslayer.ProcessExitState)
Master *os.File
}

func newProcessCacheEntry() *processCacheEntry {
Expand Down Expand Up @@ -207,7 +204,9 @@ func (c *gcsCore) ExecProcess(id string, params prot.ProcessParameters, stdioSet

var p runtime.Process

isInitProcess := len(containerEntry.Processes) == 0
containerEntry.processCacheMutex.Lock()
isInitProcess := len(containerEntry.processCache) == 0
containerEntry.processCacheMutex.Unlock()
if isInitProcess {
if err := c.writeConfigFile(id, params.OCISpecification); err != nil {
return -1, err
Expand All @@ -220,6 +219,7 @@ func (c *gcsCore) ExecProcess(id string, params prot.ProcessParameters, stdioSet

containerEntry.container = container
p = container
processEntry.Master = p.Console()

// Configure network adapters in the namespace.
for _, adapter := range containerEntry.NetworkAdapters {
Expand All @@ -244,17 +244,13 @@ func (c *gcsCore) ExecProcess(id string, params prot.ProcessParameters, stdioSet
}
c.containerCacheMutex.Unlock()

c.processCacheMutex.Lock()
containerEntry.processCacheMutex.Lock()
processEntry.ExitStatus = state
for _, hook := range processEntry.ExitHooks {
hook(state)
}
c.processCacheMutex.Unlock()
containerEntry.processCacheMutex.Unlock()
c.containerCacheMutex.Lock()
containerEntry.ExitStatus = state
for _, hook := range containerEntry.ExitHooks {
hook(state)
}
delete(c.containerCache, id)
c.containerCacheMutex.Unlock()
}()
Expand All @@ -271,6 +267,7 @@ func (c *gcsCore) ExecProcess(id string, params prot.ProcessParameters, stdioSet
if err != nil {
return -1, err
}
processEntry.Master = p.Console()

go func() {
state, err := p.Wait()
Expand All @@ -279,19 +276,19 @@ func (c *gcsCore) ExecProcess(id string, params prot.ProcessParameters, stdioSet
}
logrus.Infof("container process %d exited with exit status %d", p.Pid(), state.ExitCode())

c.processCacheMutex.Lock()
containerEntry.processCacheMutex.Lock()
processEntry.ExitStatus = state
for _, hook := range processEntry.ExitHooks {
hook(state)
}
c.processCacheMutex.Unlock()
containerEntry.processCacheMutex.Unlock()
if err := p.Delete(); err != nil {
logrus.Error(err)
}
}()
}

c.processCacheMutex.Lock()
containerEntry.processCacheMutex.Lock()
// If a processCacheEntry with the given pid already exists in the cache,
// this will overwrite it. This behavior is expected. Processes are kept in
// the cache even after they exit, which allows for exit hooks registered
Expand All @@ -304,9 +301,8 @@ func (c *gcsCore) ExecProcess(id string, params prot.ProcessParameters, stdioSet
// apply to the old process no longer makes sense, so since the old
// process's pid has been reused, its cache entry can also be reused. This
// applies to external processes as well.
c.processCache[p.Pid()] = processEntry
c.processCacheMutex.Unlock()
containerEntry.AddProcess(p.Pid())
containerEntry.processCache[p.Pid()] = processEntry
containerEntry.processCacheMutex.Unlock()
return p.Pid(), nil
}

Expand All @@ -330,18 +326,30 @@ func (c *gcsCore) SignalContainer(id string, signal oslayer.Signal) error {
}

// SignalProcess sends the signal specified in options to the given process.
func (c *gcsCore) SignalProcess(pid int, options prot.SignalProcessOptions) error {
c.processCacheMutex.Lock()
c.externalProcessCacheMutex.Lock()
if _, ok := c.processCache[pid]; !ok {
func (c *gcsCore) SignalProcess(id string, pid int, options prot.SignalProcessOptions) error {
if id == "" {
c.externalProcessCacheMutex.Lock()
if _, ok := c.externalProcessCache[pid]; !ok {
c.processCacheMutex.Unlock()
c.externalProcessCacheMutex.Unlock()
return errors.WithStack(gcserr.NewProcessDoesNotExistError(pid))
}
c.externalProcessCacheMutex.Unlock()
} else {
c.containerCacheMutex.Lock()
containerEntry := c.getContainer(id)
if containerEntry == nil {
c.containerCacheMutex.Unlock()
return errors.WithStack(gcserr.NewProcessDoesNotExistError(pid))
}
containerEntry.processCacheMutex.Lock()
if _, ok := containerEntry.processCache[pid]; !ok {
containerEntry.processCacheMutex.Unlock()
c.containerCacheMutex.Unlock()
return errors.WithStack(gcserr.NewProcessDoesNotExistError(pid))
}
containerEntry.processCacheMutex.Unlock()
c.containerCacheMutex.Unlock()
}
c.processCacheMutex.Unlock()
c.externalProcessCacheMutex.Unlock()

// Interpret signal value 0 as SIGKILL.
// TODO: Remove this special casing when we are not worried about breaking
Expand Down Expand Up @@ -395,11 +403,11 @@ func (c *gcsCore) RunExternalProcess(params prot.ProcessParameters, stdioSet *st
cmd.SetEnv(ociProcess.Env)

var relay *stdio.TtyRelay
var master *os.File
if params.EmulateConsole {
// Allocate a console for the process.
var (
consolePath string
master *os.File
)
master, consolePath, err = stdio.NewConsole()
if err != nil {
Expand Down Expand Up @@ -441,6 +449,7 @@ func (c *gcsCore) RunExternalProcess(params prot.ProcessParameters, stdioSet *st
}

processEntry := newProcessCacheEntry()
processEntry.Master = master
go func() {
if err := cmd.Wait(); err != nil {
// TODO: When cmd is a shell, and last command in the shell
Expand Down Expand Up @@ -553,18 +562,28 @@ func (c *gcsCore) RegisterContainerExitHook(id string, exitHook func(oslayer.Pro
// process may have multiple exit hooks registered for it.
// This function works for both processes that are running in a container, and
// ones that are running externally to a container.
func (c *gcsCore) RegisterProcessExitHook(pid int, exitHook func(oslayer.ProcessExitState)) error {
c.processCacheMutex.Lock()
defer c.processCacheMutex.Unlock()
c.externalProcessCacheMutex.Lock()
defer c.externalProcessCacheMutex.Unlock()
func (c *gcsCore) RegisterProcessExitHook(id string, pid int, exitHook func(oslayer.ProcessExitState)) error {
var (
entry *processCacheEntry
ok bool
)

var entry *processCacheEntry
var ok bool
entry, ok = c.processCache[pid]
if !ok {
entry, ok = c.externalProcessCache[pid]
if !ok {
if id == "" {
c.externalProcessCacheMutex.Lock()
defer c.externalProcessCacheMutex.Unlock()
if entry, ok = c.externalProcessCache[pid]; !ok {
return errors.WithStack(gcserr.NewProcessDoesNotExistError(pid))
}
} else {
c.containerCacheMutex.Lock()
defer c.containerCacheMutex.Unlock()
containerEntry := c.getContainer(id)
if containerEntry == nil {
return errors.WithStack(gcserr.NewContainerDoesNotExistError(id))
}
containerEntry.processCacheMutex.Lock()
defer containerEntry.processCacheMutex.Unlock()
if entry, ok = containerEntry.processCache[pid]; !ok {
return errors.WithStack(gcserr.NewProcessDoesNotExistError(pid))
}
}
Expand All @@ -580,6 +599,43 @@ func (c *gcsCore) RegisterProcessExitHook(pid int, exitHook func(oslayer.Process
return nil
}

func (c *gcsCore) ResizeConsole(id string, pid int, height, width uint16) error {
var (
p *processCacheEntry
ok bool
)

if id == "" {
c.externalProcessCacheMutex.Lock()
if p, ok = c.externalProcessCache[pid]; !ok {
c.externalProcessCacheMutex.Unlock()
return errors.WithStack(gcserr.NewProcessDoesNotExistError(pid))
}
c.externalProcessCacheMutex.Unlock()
} else {
c.containerCacheMutex.Lock()
containerEntry := c.getContainer(id)
if containerEntry == nil {
c.containerCacheMutex.Unlock()
return errors.WithStack(gcserr.NewContainerDoesNotExistError(id))
}
containerEntry.processCacheMutex.Lock()
if p, ok = containerEntry.processCache[pid]; !ok {
containerEntry.processCacheMutex.Unlock()
c.containerCacheMutex.Unlock()
return errors.WithStack(gcserr.NewProcessDoesNotExistError(pid))
}
containerEntry.processCacheMutex.Unlock()
c.containerCacheMutex.Unlock()
}

if err := stdio.ResizeConsole(p.Master, height, width); err != nil {
return errors.Wrapf(err, "failed to resize console for pid: %d", pid)
}

return nil
}

// setupMappedVirtualDisks is a helper function which calls into the functions
// in storage.go to set up a set of mapped virtual disks for a given container.
// It then adds them to the container's cache entry.
Expand Down
19 changes: 14 additions & 5 deletions service/gcs/runtime/runc/runc.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,25 @@ func (c *container) Pid() int {
return c.init.Pid()
}

func (c *container) Console() *os.File {
return c.init.master
}

type process struct {
c *container
pid int
relay *stdio.TtyRelay
c *container
pid int
relay *stdio.TtyRelay
master *os.File
}

func (p *process) Pid() int {
return p.pid
}

func (p *process) Console() *os.File {
return p.master
}

// NewRuntime instantiates a new runcRuntime struct.
func NewRuntime() (*runcRuntime, error) {
rtime := &runcRuntime{}
Expand Down Expand Up @@ -528,8 +537,8 @@ func (c *container) startProcess(tempProcessDir string, hasTerminal bool, stdioS
}

var relay *stdio.TtyRelay
var master *os.File
if hasTerminal {
var master *os.File
master, err = c.r.getMasterFromSocket(sockListener)
if err != nil {
cmd.Process.Kill()
Expand Down Expand Up @@ -561,5 +570,5 @@ func (c *container) startProcess(tempProcessDir string, hasTerminal bool, stdioS
if relay != nil {
relay.Start()
}
return &process{c: c, pid: pid, relay: relay}, nil
return &process{c: c, pid: pid, relay: relay, master: master}, nil
}
Loading

0 comments on commit 7556251

Please sign in to comment.