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  }