func (s *Server) Stop() {
	s.stopAPIServer()
	s.stopFUSEServer()
	s.shutdown()
}
Stop the server and any related activity. Only one of Wait or Stop should be called.
package server
import (
	"context"
	"fmt"
	"os"
	"runtime/pprof"
	"strings"
	"sync"
	"time"
	"github.com/Benchkram/errz"
	"github.com/puppetlabs/wash/activity"
	"github.com/puppetlabs/wash/analytics"
	"github.com/puppetlabs/wash/api"
	"github.com/puppetlabs/wash/fuse"
	"github.com/puppetlabs/wash/plugin"
	"github.com/puppetlabs/wash/plugin/aws"
	"github.com/puppetlabs/wash/plugin/docker"
	"github.com/puppetlabs/wash/plugin/gcp"
	"github.com/puppetlabs/wash/plugin/kubernetes"
	log "github.com/sirupsen/logrus"
)
var InternalPlugins = map[string]plugin.Root{
	"aws":        &aws.Root{},
	"docker":     &docker.Root{},
	"gcp":        &gcp.Root{},
	"kubernetes": &kubernetes.Root{},
}
type Opts struct {
	CPUProfilePath string
	LogFile        string
	LogLevel     string
	PluginConfig map[string]map[string]interface{}
}
func (o Opts) SetupLogging() (*os.File, error) {
	level, err := log.ParseLevel(o.LogLevel)
	if err != nil {
		return nil, fmt.Errorf("%v is not a valid level; use warn, info, debug, trace", o.LogLevel)
	}
	log.SetLevel(level)
	if o.LogFile != "" {
		logFH, err := os.Create(o.LogFile)
		if err != nil {
			return nil, err
		}
		log.SetOutput(logFH)
		return logFH, nil
	}
	return nil, nil
}
type controlChannels struct {
	stopCh    chan<- context.Context
	stoppedCh <-chan struct{}
}
type Server struct {
	mountpoint       string
	socket           string
	opts             Opts
	logFH            *os.File
	api              controlChannels
	fuse             controlChannels
	plugins          map[string]plugin.Root
	analyticsClient  analytics.Client
	forVerifyInstall bool
}
func New(mountpoint string, socket string, plugins map[string]plugin.Root, opts Opts) *Server {
	return &Server{
		mountpoint: mountpoint,
		socket:     socket,
		plugins:    plugins,
		opts:       opts,
	}
}
func ForVerifyInstall(mountpoint string, socket string) *Server {
	return &Server{
		mountpoint:       mountpoint,
		socket:           socket,
		forVerifyInstall: true,
		opts: Opts{
			LogLevel: "warn",
		},
	}
}
func (s *Server) Start() (bool, error) {
	var err error
	if s.logFH, err = s.opts.SetupLogging(); err != nil {
		return false, err
	}
	registry := plugin.NewRegistry()
	successfullyLoadedPlugins := true
	if !s.forVerifyInstall {
		successfullyLoadedPlugins = s.loadPlugins(registry)
		if len(registry.Plugins()) == 0 {
			return successfullyLoadedPlugins, fmt.Errorf("no plugins loaded. If you're planning on using Wash just for its external plugins, then go to https://puppetlabs.github.io/wash/docs/external-plugins")
		}
		plugin.InitCache()
		analyticsConfig, err := analytics.GetConfig()
		if err != nil {
			return successfullyLoadedPlugins, err
		}
		s.analyticsClient = analytics.NewClient(analyticsConfig)
	}
	apiServerStopCh, apiServerStoppedCh, err := api.StartAPI(
		registry,
		s.mountpoint,
		s.socket,
		s.analyticsClient,
	)
	if err != nil {
		return successfullyLoadedPlugins, err
	}
	s.api = controlChannels{stopCh: apiServerStopCh, stoppedCh: apiServerStoppedCh}
	fuseServerStopCh, fuseServerStoppedCh, err := fuse.ServeFuseFS(
		registry,
		s.mountpoint,
		s.analyticsClient,
	)
	if err != nil {
		s.stopAPIServer()
		return successfullyLoadedPlugins, err
	}
	s.fuse = controlChannels{stopCh: fuseServerStopCh, stoppedCh: fuseServerStoppedCh}
	if !s.forVerifyInstall {
		if s.opts.CPUProfilePath != "" {
			f, err := os.Create(s.opts.CPUProfilePath)
			if err != nil {
				log.Fatal(err)
			}
			errz.Fatal(pprof.StartCPUProfile(f))
		}
		if err := s.analyticsClient.Screenview("wash", analytics.Params{}); err != nil {
			log.Infof("Failed to submit the initial start-up ping: %v", err)
		}
	}
	return successfullyLoadedPlugins, nil
}
func (s *Server) stopAPIServer() {
	apiShutdownDeadline := time.Now().Add(3 * time.Second)
	apiShutdownCtx, cancelFunc := context.WithDeadline(context.Background(), apiShutdownDeadline)
	defer cancelFunc()
	s.api.stopCh <- apiShutdownCtx
	close(s.api.stopCh)
	<-s.api.stoppedCh
}
func (s *Server) stopFUSEServer() {
	close(s.fuse.stopCh)
	<-s.fuse.stoppedCh
}
func (s *Server) shutdown() {
	if s.forVerifyInstall {
		return
	}
	if s.opts.CPUProfilePath != "" {
		pprof.StopCPUProfile()
	}
	activity.CloseAll()
	doneCh := make(chan struct{})
	ticker := time.NewTicker(analytics.FlushDuration)
	defer ticker.Stop()
	go func() {
		s.analyticsClient.Flush()
		close(doneCh)
	}()
	select {
	case <-doneCh:
	case <-ticker.C:
	}
	if s.logFH != nil {
		s.logFH.Close()
	}
}
func (s *Server) Wait(sigCh chan os.Signal) {
	select {
	case <-sigCh:
		s.stopAPIServer()
		s.stopFUSEServer()
	case <-s.fuse.stoppedCh:
		s.stopAPIServer()
	case <-s.api.stoppedCh:
		s.stopFUSEServer()
	}
	s.shutdown()
}