Skip to content

Commit

Permalink
Guest shutdown support in endpointVM
Browse files Browse the repository at this point in the history
This adds neat shutdown support in the endpointVM:
* cleans up Views, Managers and sessions in portlayer
* cleans up session in personality
* cleans up and purges all active sessions in vicadmin

Updates the way tether waits for sessions to exit.
Updates vic-machine to use guest shutdown for endpointVM

Unifies diskmanager usage on a single instance for the storage
component. This is mostly unrelated but is the correct usage given locking
assumptions.

Fix tether tests for non-root use
  • Loading branch information
hickeng committed Feb 8, 2018
1 parent c2dff47 commit d0cbc66
Show file tree
Hide file tree
Showing 32 changed files with 445 additions and 115 deletions.
7 changes: 6 additions & 1 deletion cmd/docker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package main

import (
"context"
"crypto/tls"
"crypto/x509"
"flag"
Expand Down Expand Up @@ -120,12 +121,16 @@ func main() {
serveAPIWait := make(chan error)
go api.Wait(serveAPIWait)

// signal.Trap explicitly calls os.Exit so an exit logic has to be rolled in here
signal.Trap(func() {
log.Info("Closing down docker personality")

api.Close()
plEventMonitor.Stop()
vicbackends.Finalize(context.Background())
})

<-serveAPIWait
plEventMonitor.Stop()
}

func handleFlags() bool {
Expand Down
10 changes: 9 additions & 1 deletion cmd/tether/main_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,22 @@ func halt() {

func startSignalHandler() {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGHUP)
signal.Notify(sigs, syscall.SIGHUP, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGPWR, syscall.SIGTERM, syscall.SIGINT)

go func() {
for s := range sigs {
switch s {
case syscall.SIGHUP:
log.Infof("Reloading tether configuration")
tthr.Reload()
case syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGPWR:
log.Infof("Stopping tether via signal %s", s.String())
tthr.Stop()
case syscall.SIGTERM, syscall.SIGINT:
log.Infof("Stopping system in lieu of restart handling via signal %s", s.String())
// TODO: update this to adjust power off handling for reboot
// this should be in guest reboot rather than power cycle
tthr.Stop()
default:
log.Infof("%s signal not defined", s.String())
}
Expand Down
4 changes: 4 additions & 0 deletions cmd/tether/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ func (m *mockery) Stop() error {
return nil
}

func (m *mockery) Wait(ctx context.Context) error {
return nil
}

func (m *mockery) Register(name string, config tether.Extension) {
}

Expand Down
90 changes: 84 additions & 6 deletions cmd/vic-init/main_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
package main

import (
"context"
"errors"
"os"
"os/signal"
"runtime/debug"
"syscall"
"time"
Expand All @@ -25,6 +28,7 @@ import (

"github.com/vmware/govmomi/toolbox"
"github.com/vmware/vic/lib/tether"
"github.com/vmware/vic/lib/tether/shared"
viclog "github.com/vmware/vic/pkg/log"
"github.com/vmware/vic/pkg/log/syslog"
"github.com/vmware/vic/pkg/logmgr"
Expand Down Expand Up @@ -79,6 +83,8 @@ func main() {
extraconfig.Decode(src, &config)
debugLevel = config.Diagnostics.DebugLevel

startSignalHandler()

logcfg := viclog.NewLoggingConfig()
if debugLevel > 0 {
logcfg.Level = log.DebugLevel
Expand Down Expand Up @@ -136,33 +142,80 @@ func main() {
log.Info("Clean exit from init")
}

// exitTether signals the current process, which triggers tether.Stop and the killing of its children.
// NOTE: I don't like having this here and it really needs to be moved into an interface that
// can be provided to toolbox for system callbacks. While this could be part of the Operations
// interface I think I'd rather have a separate one specifically for the possible toolbox interactions.
func exitTether() error {
defer trace.End(trace.Begin(""))

p, err := os.FindProcess(os.Getpid())
if err != nil {
return err
}

if err = p.Signal(syscall.SIGUSR2); err != nil {
return err
}

return err
}

// exit cleanly shuts down the system
func halt() {
func halt() error {
log.Infof("Powering off the system")
if debugLevel > 0 {

err := exitTether()
if err != nil {
log.Warn(err)
}

if debugLevel > 2 {
log.Info("Squashing power off for debug init")
return
return errors.New("debug config suppresses shutdown")
}

timeout, cancel := context.WithTimeout(context.Background(), shared.GuestShutdownTimeout)
err = tthr.Wait(timeout)
cancel()

syscall.Sync()
syscall.Reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)

return err
}

func reboot() {
func reboot() error {
log.Infof("Rebooting the system")
if debugLevel > 0 {

err := exitTether()
if err != nil {
log.Warn(err)
}

if debugLevel > 2 {
log.Info("Squashing reboot for debug init")
return
return errors.New("debug config suppresses reboot")
}

timeout, cancel := context.WithTimeout(context.Background(), shared.GuestRebootTimeout)
err = tthr.Wait(timeout)
cancel()

syscall.Sync()
syscall.Reboot(syscall.LINUX_REBOOT_CMD_RESTART)

return err
}

func configureToolbox(t *tether.Toolbox) *tether.Toolbox {
cmd := t.Service.Command
cmd.ProcessStartCommand = startCommand

t.Power.Halt.Handler = halt
t.Power.Reboot.Handler = reboot
t.Power.Suspend.Handler = exitTether

return t
}

Expand Down Expand Up @@ -201,3 +254,28 @@ func defaultIP() string {

return toolbox.DefaultIP()
}

func startSignalHandler() {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGHUP, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGPWR, syscall.SIGTERM, syscall.SIGINT)

go func() {
for s := range sigs {
switch s {
case syscall.SIGHUP:
log.Infof("Reloading tether configuration")
tthr.Reload()
case syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGPWR:
log.Infof("Stopping tether via signal %s", s.String())
tthr.Stop()
case syscall.SIGTERM, syscall.SIGINT:
log.Infof("Stopping system in lieu of restart handling via signal %s", s.String())
// TODO: update this to adjust power off handling for reboot
// this should be in guest reboot rather than power cycle
tthr.Stop()
default:
log.Infof("%s signal not defined", s.String())
}
}
}()
}
2 changes: 2 additions & 0 deletions cmd/vicadmin/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,8 @@ func (s *server) serve() error {
func (s *server) stop() error {
defer trace.End(trace.Begin(""))

s.uss.Destroy()

if s.l != nil {
err := s.l.Close()
s.l = nil
Expand Down
14 changes: 14 additions & 0 deletions cmd/vicadmin/usersession.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ func (u *UserSessionStore) Add(id string, config *session.Config, vs *session.Se
func (u *UserSessionStore) Delete(id string) {
u.mutex.Lock()
defer u.mutex.Unlock()

us := u.sessions[id]
if us != nil && us.vsphere != nil {
log.Infof("Logging out vSphere session for %s", id)
us.vsphere.Logout(context.Background())
}

delete(u.sessions, id)
}

Expand Down Expand Up @@ -112,6 +119,13 @@ func (u *UserSessionStore) reaper() {
}
}

// Destroy will logout and delete all sessions in the store, irrespective of expiration
func (u *UserSessionStore) Destroy() {
for id := range u.sessions {
u.Delete(id)
}
}

// NewUserSessionStore creates & initializes a UserSessionStore and starts a session reaper in the background
func NewUserSessionStore() *UserSessionStore {
u := &UserSessionStore{
Expand Down
8 changes: 1 addition & 7 deletions cmd/vicadmin/vicadm.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,15 +612,9 @@ func main() {
}

log.Infof("listening on %s", s.addr)
signals := []syscall.Signal{
syscall.SIGTERM,
syscall.SIGINT,
}

sigchan := make(chan os.Signal, 1)
for _, signum := range signals {
signal.Notify(sigchan, signum)
}
signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT)

go func() {
signal := <-sigchan
Expand Down
2 changes: 1 addition & 1 deletion infra/scripts/replace-running-components.sh
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function replace-component() {
if [[ $1 == "vic-init" ]]; then
on-vch systemctl restart vic-init
else
on-vch kill -9 $pid
on-vch kill -TERM $pid
fi
}

Expand Down
14 changes: 14 additions & 0 deletions lib/apiservers/engine/backends/backends.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
"github.com/vmware/vic/lib/imagec"
"github.com/vmware/vic/pkg/errors"
"github.com/vmware/vic/pkg/registry"
"github.com/vmware/vic/pkg/version"
"github.com/vmware/vic/pkg/vsphere/session"
"github.com/vmware/vic/pkg/vsphere/sys"
)
Expand Down Expand Up @@ -149,6 +150,18 @@ func Init(portLayerAddr, product string, port uint, config *config.VirtualContai
return nil
}

// Finalize performs cleanup before a graceful exit of the API server.
// In this case that means logging out the dynamic config session if any.
func Finalize(ctx context.Context) error {
log.Info("Shutting down docker API server backend")

if vchConfig != nil && vchConfig.sess != nil {
vchConfig.sess.Logout(ctx)
}

return nil
}

func hydrateCaches() error {
const waiters = 3

Expand Down Expand Up @@ -527,6 +540,7 @@ func newSession(ctx context.Context, config *config.VirtualContainerHostConfigSp
User: url.UserPassword(config.Username, config.Token),
Thumbprint: config.TargetThumbprint,
Keepalive: defaultSessionKeepAlive,
UserAgent: version.UserAgent("vic-dynamic-config"),
}

sess := session.NewSession(sessCfg)
Expand Down
10 changes: 3 additions & 7 deletions lib/apiservers/portlayer/restapi/configure_port_layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import (
"github.com/vmware/vic/lib/apiservers/portlayer/restapi/operations"
"github.com/vmware/vic/lib/apiservers/portlayer/restapi/options"
"github.com/vmware/vic/lib/portlayer"
"github.com/vmware/vic/lib/portlayer/exec"
"github.com/vmware/vic/pkg/version"
"github.com/vmware/vic/pkg/vsphere/session"
)
Expand All @@ -59,7 +58,7 @@ var portlayerhandlers = []handler{

var apiServers []*graceful.Server

const stopTimeout = time.Second * 3
const stopTimeout = time.Second * 20

func configureFlags(api *operations.PortLayerAPI) {
api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{
Expand Down Expand Up @@ -96,11 +95,8 @@ func configureAPI(api *operations.PortLayerAPI) http.Handler {
api.ServerShutdown = func() {
log.Infof("Shutting down port-layer-server")

// stop the event collectors
collectors := exec.Config.EventManager.Collectors()
for _, c := range collectors {
c.Stop()
}
// shut down any component specific long running daemon tasks
portlayer.Finalize(context.Background())

// Logout the session
if err := sess.Logout(ctx); err != nil {
Expand Down
Loading

0 comments on commit d0cbc66

Please sign in to comment.