bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/cmd/synsec/serve.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/signal"
     7  	"syscall"
     8  	"time"
     9  
    10  	"github.com/coreos/go-systemd/daemon"
    11  	"github.com/pkg/errors"
    12  
    13  	"bitbucket.org/Aishee/synsec/pkg/csconfig"
    14  	leaky "bitbucket.org/Aishee/synsec/pkg/leakybucket"
    15  	"bitbucket.org/Aishee/synsec/pkg/types"
    16  	log "github.com/sirupsen/logrus"
    17  	"gopkg.in/tomb.v2"
    18  	//"github.com/sevlyar/go-daemon"
    19  )
    20  
    21  //debugHandler is kept as a dev convenience : it shuts down and serialize internal state
    22  func debugHandler(sig os.Signal, cConfig *csconfig.Config) error {
    23  	var tmpFile string
    24  	var err error
    25  	//stop go routines
    26  	if err := ShutdownSynsecRoutines(); err != nil {
    27  		log.Warningf("Failed to shut down routines: %s", err)
    28  	}
    29  	//todo : properly stop acquis with the tail readers
    30  	if tmpFile, err = leaky.DumpBucketsStateAt(time.Now(), cConfig.Synsec.BucketStateDumpDir, buckets); err != nil {
    31  		log.Warningf("Failed dumping bucket state : %s", err)
    32  	}
    33  	if err := leaky.ShutdownAllBuckets(buckets); err != nil {
    34  		log.Warningf("while shutting down routines : %s", err)
    35  	}
    36  	log.Printf("shutdown is finished buckets are in %s", tmpFile)
    37  	return nil
    38  }
    39  
    40  func reloadHandler(sig os.Signal, cConfig *csconfig.Config) error {
    41  	var tmpFile string
    42  	var err error
    43  
    44  	//stop go routines
    45  	if !cConfig.DisableAgent {
    46  		if err := shutdownSynsec(); err != nil {
    47  			log.Fatalf("Failed to shut down synsec routines: %s", err)
    48  		}
    49  		if cConfig.Synsec != nil && cConfig.Synsec.BucketStateDumpDir != "" {
    50  			if tmpFile, err = leaky.DumpBucketsStateAt(time.Now(), cConfig.Synsec.BucketStateDumpDir, buckets); err != nil {
    51  				log.Fatalf("Failed dumping bucket state : %s", err)
    52  			}
    53  		}
    54  
    55  		if err := leaky.ShutdownAllBuckets(buckets); err != nil {
    56  			log.Fatalf("while shutting down routines : %s", err)
    57  		}
    58  	}
    59  
    60  	if !cConfig.DisableAPI {
    61  		if err := shutdownAPI(); err != nil {
    62  			log.Fatalf("Failed to shut down api routines: %s", err)
    63  		}
    64  	}
    65  
    66  	/*
    67  	 re-init tombs
    68  	*/
    69  	acquisTomb = tomb.Tomb{}
    70  	parsersTomb = tomb.Tomb{}
    71  	bucketsTomb = tomb.Tomb{}
    72  	outputsTomb = tomb.Tomb{}
    73  	apiTomb = tomb.Tomb{}
    74  	synsecTomb = tomb.Tomb{}
    75  
    76  	if err := LoadConfig(cConfig); err != nil {
    77  		log.Fatalf(err.Error())
    78  	}
    79  	// Configure logging
    80  	if err = types.SetDefaultLoggerConfig(cConfig.Common.LogMedia, cConfig.Common.LogDir, *cConfig.Common.LogLevel); err != nil {
    81  		log.Fatal(err.Error())
    82  	}
    83  
    84  	if !cConfig.DisableAPI {
    85  		apiServer, err := initAPIServer(cConfig)
    86  		if err != nil {
    87  			return fmt.Errorf("unable to init api server: %s", err)
    88  		}
    89  
    90  		serveAPIServer(apiServer)
    91  	}
    92  
    93  	if !cConfig.DisableAgent {
    94  		csParsers, err := initSynsec(cConfig)
    95  		if err != nil {
    96  			return fmt.Errorf("unable to init synsec: %s", err)
    97  		}
    98  		//restore bucket state
    99  		if tmpFile != "" {
   100  			log.Warningf("we are now using %s as a state file", tmpFile)
   101  			cConfig.Synsec.BucketStateFile = tmpFile
   102  		}
   103  		//reload the simulation state
   104  		if err := cConfig.LoadSimulation(); err != nil {
   105  			log.Errorf("reload error (simulation) : %s", err)
   106  		}
   107  		serveSynsec(csParsers, cConfig)
   108  	}
   109  
   110  	log.Printf("Reload is finished")
   111  	//delete the tmp file, it's safe now :)
   112  	if tmpFile != "" {
   113  		if err := os.Remove(tmpFile); err != nil {
   114  			log.Warningf("Failed to delete temp file (%s) : %s", tmpFile, err)
   115  		}
   116  	}
   117  	return nil
   118  }
   119  
   120  func ShutdownSynsecRoutines() error {
   121  	var reterr error
   122  
   123  	log.Debugf("Shutting down synsec sub-routines")
   124  	acquisTomb.Kill(nil)
   125  	log.Debugf("waiting for acquisition to finish")
   126  	if err := acquisTomb.Wait(); err != nil {
   127  		log.Warningf("Acquisition returned error : %s", err)
   128  		reterr = err
   129  	}
   130  	log.Debugf("acquisition is finished, wait for parser/bucket/ouputs.")
   131  	parsersTomb.Kill(nil)
   132  	if err := parsersTomb.Wait(); err != nil {
   133  		log.Warningf("Parsers returned error : %s", err)
   134  		reterr = err
   135  	}
   136  	log.Debugf("parsers is done")
   137  	time.Sleep(1 * time.Second) //ugly workaround for now to ensure PourItemtoholders are finished
   138  	bucketsTomb.Kill(nil)
   139  	if err := bucketsTomb.Wait(); err != nil {
   140  		log.Warningf("Buckets returned error : %s", err)
   141  		reterr = err
   142  	}
   143  	log.Debugf("buckets is done")
   144  	time.Sleep(1 * time.Second) //ugly workaround for now
   145  	outputsTomb.Kill(nil)
   146  	if err := outputsTomb.Wait(); err != nil {
   147  		log.Warningf("Ouputs returned error : %s", err)
   148  		reterr = err
   149  
   150  	}
   151  	log.Debugf("outputs are done")
   152  	//everything is dead johny
   153  	synsecTomb.Kill(nil)
   154  
   155  	return reterr
   156  }
   157  
   158  func shutdownAPI() error {
   159  	log.Debugf("shutting down api via Tomb")
   160  	apiTomb.Kill(nil)
   161  	if err := apiTomb.Wait(); err != nil {
   162  		return err
   163  	}
   164  	log.Debugf("done")
   165  	return nil
   166  }
   167  
   168  func shutdownSynsec() error {
   169  	log.Debugf("shutting down synsec via Tomb")
   170  	synsecTomb.Kill(nil)
   171  	if err := synsecTomb.Wait(); err != nil {
   172  		return err
   173  	}
   174  	log.Debugf("done")
   175  	return nil
   176  }
   177  
   178  func termHandler(sig os.Signal) error {
   179  	if err := shutdownSynsec(); err != nil {
   180  		log.Errorf("Error encountered while shutting down synsec: %s", err)
   181  	}
   182  	if err := shutdownAPI(); err != nil {
   183  		log.Errorf("Error encountered while shutting down api: %s", err)
   184  	}
   185  	log.Debugf("termHandler done")
   186  	return nil
   187  }
   188  
   189  func HandleSignals(cConfig *csconfig.Config) {
   190  	signalChan := make(chan os.Signal, 1)
   191  	signal.Notify(signalChan,
   192  		syscall.SIGHUP,
   193  		syscall.SIGTERM)
   194  
   195  	exitChan := make(chan int)
   196  	go func() {
   197  		defer types.CatchPanic("synsec/HandleSignals")
   198  		for {
   199  			s := <-signalChan
   200  			switch s {
   201  			// kill -SIGHUP XXXX
   202  			case syscall.SIGHUP:
   203  				log.Warningf("SIGHUP received, reloading")
   204  				if err := reloadHandler(s, cConfig); err != nil {
   205  					log.Fatalf("Reload handler failure : %s", err)
   206  				}
   207  			// kill -SIGTERM XXXX
   208  			case syscall.SIGTERM:
   209  				log.Warningf("SIGTERM received, shutting down")
   210  				if err := termHandler(s); err != nil {
   211  					log.Fatalf("Term handler failure : %s", err)
   212  				}
   213  				exitChan <- 0
   214  			}
   215  		}
   216  	}()
   217  
   218  	code := <-exitChan
   219  	log.Warningf("Synsec service shutting down")
   220  	os.Exit(code)
   221  }
   222  
   223  func Serve(cConfig *csconfig.Config) error {
   224  	acquisTomb = tomb.Tomb{}
   225  	parsersTomb = tomb.Tomb{}
   226  	bucketsTomb = tomb.Tomb{}
   227  	outputsTomb = tomb.Tomb{}
   228  	apiTomb = tomb.Tomb{}
   229  	synsecTomb = tomb.Tomb{}
   230  
   231  	if !cConfig.DisableAPI {
   232  		apiServer, err := initAPIServer(cConfig)
   233  		if err != nil {
   234  			return errors.Wrap(err, "api server init")
   235  		}
   236  		if !flags.TestMode {
   237  			serveAPIServer(apiServer)
   238  		}
   239  	}
   240  
   241  	if !cConfig.DisableAgent {
   242  		csParsers, err := initSynsec(cConfig)
   243  		if err != nil {
   244  			return errors.Wrap(err, "synsec init")
   245  		}
   246  		/* if it's just linting, we're done */
   247  		if !flags.TestMode {
   248  			serveSynsec(csParsers, cConfig)
   249  		}
   250  	}
   251  	if flags.TestMode {
   252  		log.Infof("test done")
   253  		os.Exit(0)
   254  	}
   255  
   256  	if cConfig.Common != nil && cConfig.Common.Daemonize {
   257  		sent, err := daemon.SdNotify(false, daemon.SdNotifyReady)
   258  		if !sent || err != nil {
   259  			log.Errorf("Failed to notify(sent: %v): %v", sent, err)
   260  		}
   261  		/*wait for signals*/
   262  		HandleSignals(cConfig)
   263  	} else {
   264  		for {
   265  			select {
   266  			case <-apiTomb.Dead():
   267  				log.Infof("api shutdown")
   268  				os.Exit(0)
   269  			case <-synsecTomb.Dead():
   270  				log.Infof("synsec shutdown")
   271  				os.Exit(0)
   272  			}
   273  		}
   274  	}
   275  	return nil
   276  }