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 }