// Loads the config file from disk or from the specified defaultConfig location
// when either the local file is missing or overwriteWithInitial is true.
func loadConfig(defaultConfig string, overwriteWithInitial bool) *util.Config {
	config := util.NewConfig(ConfigName)

	if len(defaultConfig) > 0 && (config.NeedsSave || overwriteWithInitial) {
		var err error
		var configSource io.ReadCloser
		defer func() {
			if configSource != nil {
				configSource.Close()
			}
		}()

		// If default config uses "." we search for it next to the location where the launcher executable resides.
		if defaultConfig == "." {
			defaultConfig = filepath.Join(filepath.Dir(AppImagePath), ConfigName)
		}

		if isHttpUrl, _ := regexp.MatchString("^(?i)http(s|)://.+", defaultConfig); isHttpUrl {
			util.Out("Downloading: %v", defaultConfig)
			var response *http.Response
			if response, err = http.Get(defaultConfig); err == nil {
				if response.StatusCode == 200 {
					configSource = response.Body
				} else {
					err = fmt.Errorf(response.Status)
				}
			}
		} else {
			util.Out("Copying: %v", defaultConfig)
			configSource, err = os.Open(defaultConfig)
		}

		if err != nil {
			panic(fmt.Sprintf("Failed loading %v;\nCause: %v; => exiting.", defaultConfig, err))
		}

		if configFile, err := os.Create(ConfigName); err == nil {
			defer configFile.Close()

			if _, err = io.Copy(configFile, configSource); err == nil {
				config = util.NewConfig(ConfigName)
			}
		} else {
			panic(fmt.Sprintf("Failed creating initial %v from %v;\ncause: %v; => exiting.", ConfigName, defaultConfig, err))
		}
	}

	return config
}
Esempio n. 2
0
// Runs the mode that is activated within the specified config instance,
// returning false if the mode stopped due to a kill or interrupt and true when the mode stopped due to an error.
func RunConfiguredMode(config *util.Config) bool {
	exitRequested := make(chan bool, 2)

	// Getting the configured run mode
	executableMode := GetConfiguredMode(config)
	util.Out("Starting mode %v", executableMode.Name())
	callListeners(executableMode, ModeStarting, config)
	err := executableMode.Start(config)

	if err != nil {
		util.Out("ERROR: Failed to start mode '%v'; Cause: %v", executableMode.Name(), err)
		return false
	} else {
		callListeners(executableMode, ModeStarted, config)
		defer callListeners(executableMode, ModeStopped, config)
	}

	// Waiting on Interrupt or Kill and stop the run mode when received.
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt, os.Kill)

	go func() {
		sig := <-signals // This channel receives only Interrupt or Kill and blocks until one is received.
		util.Out("Received signal: %v, stopping...", sig)
		exitRequested <- true
		if executableMode.Status().Get() != ModeStopped {
			executableMode.Stop()
		}
	}()

	util.Out("STARTED mode %v", executableMode.Name())

	// Waiting for the run mode to stop
	for executableMode.Status().Get() != ModeStopped {
		time.Sleep(time.Millisecond * 100)
	}
	exitRequested <- false
	signals <- os.Interrupt

	util.Out("STOPPED mode %v", executableMode.Name())

	// Returning true if we want to re-run.
	isExistRequested := <-exitRequested
	<-exitRequested

	return !isExistRequested
}
// Handles the working directory that is used.
func handleWorkingDirectory(dir string) {
	wd, _ := os.Getwd()
	dir = filepath.FromSlash(dir)

	if len(dir) > 0 && dir != wd {
		util.Out("Changing working directory to %v", dir)

		if fi, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
			util.Out("WARN: Working directory %v does not exist, creating it now.", dir)
			if err = os.MkdirAll(dir, os.ModeDir); err != nil {
				panic(fmt.Sprintf("Failed creating working directory %v, stopping here to prevent any damage. Cause: %v", dir, err))
			}
		} else if !fi.IsDir() {
			panic(fmt.Sprintf("%v is not a directory, cannot change working directory, stopping here to prevent any damage. Cause: %v", dir, err))
		}

		if err := os.Chdir(dir); err != nil {
			wd, _ = os.Getwd()
			panic(fmt.Sprintf("Failed changing working directory, stopping here to prevent any damage. Cause: %v ; CWD is %v", err, wd))
		}
	}
}
// Listens for key codes.
func listenForKeyboardInput(config *util.Config, subsequentRestarts *int64) {
	var keyCode = make([]byte, 1)
	util.Out("Listening for keys: [%s]: Print Stacktrace | [%s]: Restart client.", "D+Return", "R+Return")
	for {
		if n, err := os.Stdin.Read(keyCode); err == nil && n == 1 {
			switch keyCode[0] {
			case 'r', 'R':
				*subsequentRestarts = 0
				modes.GetConfiguredMode(config).Stop()
			case 'd', 'D':
				util.PrintAllStackTraces()
			}
		} else {
			return
		}
	}
}
// Is the applications main run loop.
func Run() {
	util.FlatOut("\n%s %s\n---------------------------", AppName, AppVersion)

	AppImagePath, _ = filepath.Abs(os.Args[0])

	modeNames := make([]string, len(modes.AllModes))
	for i, m := range modes.AllModes {
		modeNames[i] = m.Name()
	}

	help := flag.Bool("help", false, "Show this help.")
	autoStart := flag.Bool("autostart", false, "Automatically start this app when the OS runs (implies '-persist=true').")
	runMode := flag.String("runMode", "client", "Specifies the mode in which the launcher operates if not already set "+
		"inside '"+ConfigName+"'. Supporte modes are "+fmt.Sprintf("%v", modeNames)+".")

	url := flag.String("url", "", "Specifies the URL to Jenkins server if not already set inside '"+ConfigName+"'.")
	name := flag.String("name", "", "Specifies the name of this node in Jenkins (defaults to [hostname] if not specified).")
	create := flag.Bool("create", false, "Enables the auto creation of a Jenkins node if it is missing.")
	secretKey := flag.String("secret", "", "Specifies the secret key to use in client mode when starting the Jenkins client.")
	acceptAnyCert := flag.Bool("anyCert", false, "Disabled cert verification for TLS connections with Jenkins (this is not secure at all).")

	dir := flag.String("directory", "", "Changes the current working directory before performing any other operations.")
	saveChanges := flag.Bool("persist", false, "Stores any CLI config overrides inside '"+ConfigName+"'.")
	defaultConfig := flag.String("defaultConfig", "", "Loads the initial config from the specified path or URL (http[s]). "+
		"Does nothing when '"+ConfigName+"' exists already.")
	overwrite := flag.Bool("overwrite", false, "Overwrites '"+ConfigName+"' with the content from initial config "+
		"(requires '-defaultConfig=...', implies '-persist=true').")

	flag.CommandLine.Init(AppName, flag.ContinueOnError)
	flag.CommandLine.SetOutput(os.Stdout)
	flag.Parse()

	handleWorkingDirectory(*dir)

	if alreadyRunning := CheckIfAlreadyRunning(); alreadyRunning {
		util.Out("WARN: Another launcher is already running with the same configuration. Exiting...")
		return
	} else {
		defer alreadyRunning.Close()
	}

	config := loadConfig(*defaultConfig, *overwrite)

	if len(*runMode) > 0 {
		config.RunMode = *runMode
	}
	if len(*url) > 0 {
		config.CIHostURI = *url
	}
	if len(*secretKey) > 0 {
		config.SecretKey = *secretKey
	}
	if len(*name) > 0 {
		config.ClientName = *name
	}
	if *create {
		config.CreateClientIfMissing = true
	}
	if *acceptAnyCert {
		config.CIAcceptAnyCert = true
	}

	saveConfigIfRequired := func() {
		if config.NeedsSave || *saveChanges || *autoStart || *overwrite {
			config.Save(ConfigName)
		}
	}

	if *help {
		saveConfigIfRequired()
		fmt.Printf(AppDescription, ConfigName, filepath.Base(os.Args[0]))
		flag.PrintDefaults()
		return
	}

	environment.RunPreparers(config)

	abort := !modes.GetConfiguredMode(config).IsConfigAcceptable(config)
	saveConfigIfRequired()

	if abort {
		return
	}

	restartCount := int64(0)
	sleepTimePerRestart := int64(time.Second * time.Duration(config.SleepTimeSecondsBetweenFailures))
	runTimeAfterResettingRestartCount := time.Hour * 2

	timeOfLastStart := time.Now()

	go listenForKeyboardInput(config, &restartCount)

	for modes.RunConfiguredMode(config) {
		util.FlatOut("\n:::::::::::::::::::::::::::::::::\n::  %25s  ::\n:::::::::::::::::::::::::::::::::\n", "Restarting Jenkins Client")

		if timeOfLastStart.Before(time.Now().Add(-runTimeAfterResettingRestartCount)) {
			restartCount = 0
		}

		if sleepTime := time.Duration(restartCount * sleepTimePerRestart); sleepTime > 0 {
			util.FlatOut("Sleeping %v seconds before restarting the client.\n\n", sleepTime.Seconds())
			time.Sleep(sleepTime)
		}

		restartCount++
		timeOfLastStart = time.Now()
	}
}