github.com/getgauge/gauge@v1.6.9/api/api.go (about) 1 /*---------------------------------------------------------------- 2 * Copyright (c) ThoughtWorks, Inc. 3 * Licensed under the Apache License, Version 2.0 4 * See LICENSE in the project root for license information. 5 *----------------------------------------------------------------*/ 6 7 package api 8 9 import ( 10 "fmt" 11 "os" 12 "strconv" 13 "time" 14 15 "github.com/getgauge/common" 16 "github.com/getgauge/gauge/api/infoGatherer" 17 "github.com/getgauge/gauge/config" 18 "github.com/getgauge/gauge/conn" 19 "github.com/getgauge/gauge/logger" 20 "github.com/getgauge/gauge/manifest" 21 "github.com/getgauge/gauge/runner" 22 "github.com/getgauge/gauge/util" 23 ) 24 25 type StartChannels struct { 26 // this will hold the runner 27 RunnerChan chan runner.Runner 28 // this will hold the error while creating runner 29 ErrorChan chan error 30 // this holds a flag based on which the runner is terminated 31 KillChan chan bool 32 } 33 34 // StartAPI calls StartAPIService and returns the channels 35 func StartAPI(debug bool) *StartChannels { 36 startChan := &StartChannels{RunnerChan: make(chan runner.Runner), ErrorChan: make(chan error), KillChan: make(chan bool)} 37 sig := &infoGatherer.SpecInfoGatherer{} 38 go startAPIService(0, startChan, sig, debug) 39 return startChan 40 } 41 42 // StartAPIService starts the Gauge API service 43 func startAPIService(port int, startChannels *StartChannels, sig *infoGatherer.SpecInfoGatherer, debug bool) { 44 startAPIServiceWithoutRunner(port, startChannels, sig) 45 46 runner, err := ConnectToRunner(startChannels.KillChan, debug) 47 if err != nil { 48 startChannels.ErrorChan <- err 49 return 50 } 51 startChannels.RunnerChan <- runner 52 } 53 54 func startAPIServiceWithoutRunner(port int, startChannels *StartChannels, sig *infoGatherer.SpecInfoGatherer) { 55 apiHandler := &gaugeAPIMessageHandler{specInfoGatherer: sig} 56 gaugeConnectionHandler, err := conn.NewGaugeConnectionHandler(port, apiHandler) 57 if err != nil { 58 startChannels.ErrorChan <- fmt.Errorf("Connection error. %s", err.Error()) 59 return 60 } 61 if port == 0 { 62 if err := common.SetEnvVariable(common.APIPortEnvVariableName, strconv.Itoa(gaugeConnectionHandler.ConnectionPortNumber())); err != nil { 63 startChannels.ErrorChan <- fmt.Errorf("Failed to set Env variable %s. %s", common.APIPortEnvVariableName, err.Error()) 64 return 65 } 66 } 67 go gaugeConnectionHandler.HandleMultipleConnections() 68 } 69 70 func ConnectToRunner(killChannel chan bool, debug bool) (runner.Runner, error) { 71 manifest, err := manifest.ProjectManifest() 72 if err != nil { 73 return nil, err 74 } 75 runner, connErr := runner.Start(manifest, 0, killChannel, debug) 76 if connErr != nil { 77 return nil, connErr 78 } 79 80 return runner, nil 81 } 82 83 func runAPIServiceIndefinitely(port int, specDirs []string) { 84 startChan := &StartChannels{RunnerChan: make(chan runner.Runner), ErrorChan: make(chan error), KillChan: make(chan bool)} 85 86 sig := &infoGatherer.SpecInfoGatherer{SpecDirs: specDirs} 87 sig.Init() 88 go startAPIServiceWithoutRunner(port, startChan, sig) 89 go checkParentIsAlive(startChan) 90 91 logger.Infof(true, "Gauge daemon initialized and listening on port: %d", port) 92 93 for { 94 select { 95 case runner := <-startChan.RunnerChan: 96 logger.Infof(true, "Got a kill message. Killing runner.") 97 err := runner.Kill() 98 if err != nil { 99 logger.Errorf(true, "Unable to kill runner with PID %d. %s", runner.Pid(), err.Error()) 100 } 101 case err := <-startChan.ErrorChan: 102 logger.Fatalf(true, "Killing Gauge daemon. %v", err.Error()) 103 } 104 } 105 } 106 107 func checkParentIsAlive(startChannels *StartChannels) { 108 parentProcessID := os.Getppid() 109 for { 110 if !util.IsProcessRunning(parentProcessID) { 111 startChannels.ErrorChan <- fmt.Errorf("Parent process with pid %d has terminated.", parentProcessID) 112 return 113 } 114 time.Sleep(1 * time.Second) 115 } 116 } 117 118 // RunInBackground runs Gauge in daemonized mode on the given apiPort 119 func RunInBackground(apiPort string, specDirs []string) { 120 var port int 121 var err error 122 if apiPort != "" { 123 port, err = strconv.Atoi(apiPort) 124 if err != nil { 125 logger.Fatalf(true, "Invalid port number: %s", apiPort) 126 } 127 os.Setenv(common.APIPortEnvVariableName, apiPort) 128 } else { 129 port, err = conn.GetPortFromEnvironmentVariable(common.APIPortEnvVariableName) 130 if err != nil { 131 logger.Fatalf(true, "Failed to start API Service. %s \n", err.Error()) 132 } 133 } 134 runAPIServiceIndefinitely(port, specDirs) 135 } 136 137 func Start(specsDir []string) *conn.GaugeConnectionHandler { 138 sig := &infoGatherer.SpecInfoGatherer{SpecDirs: specsDir} 139 sig.Init() 140 apiHandler := &gaugeAPIMessageHandler{specInfoGatherer: sig} 141 gaugeConnectionHandler, err := conn.NewGaugeConnectionHandler(0, apiHandler) 142 if err != nil { 143 logger.Fatal(true, err.Error()) 144 } 145 errChan := make(chan error) 146 go func() { 147 _, err := gaugeConnectionHandler.AcceptConnection(config.RunnerConnectionTimeout(), errChan) 148 if err != nil { 149 logger.Fatal(true, err.Error()) 150 } 151 }() 152 go func() { 153 e := <-errChan 154 logger.Fatal(true, e.Error()) 155 }() 156 return gaugeConnectionHandler 157 }