예제 #1
0
func (mr *MultiRunner) requestBuild(runner *common.RunnerConfig) *common.Build {
	if runner == nil {
		return nil
	}

	if !mr.isHealthy(runner) {
		return nil
	}

	count := mr.buildsForRunner(runner)
	limit := helpers.NonZeroOrDefault(runner.Limit, math.MaxInt32)
	if count >= limit {
		return nil
	}

	buildData, healthy := common.GetBuild(*runner)
	if healthy {
		mr.makeHealthy(runner)
	} else {
		mr.makeUnhealthy(runner)
	}

	if buildData == nil {
		return nil
	}

	mr.debugln("Received new build for", runner.ShortDescription(), "build", buildData.ID)
	newBuild := &common.Build{
		GetBuildResponse: *buildData,
		Runner:           runner,
		BuildAbort:       mr.abortBuilds,
	}
	return newBuild
}
예제 #2
0
func (mr *MultiRunner) makeUnhealthy(runner *common.RunnerConfig) {
	health := mr.getHealth(runner)
	health.failures++

	if health.failures >= common.HealthyChecks {
		mr.errorln("Runner", runner.ShortDescription(), "is not healthy and will be disabled!")
	}
}
예제 #3
0
func (mr *MultiRunner) isHealthy(runner *common.RunnerConfig) bool {
	health := mr.getHealth(runner)
	if health.failures < common.HealthyChecks {
		return true
	}

	if time.Since(health.lastCheck) > common.HealthCheckInterval*time.Second {
		mr.errorln("Runner", runner.ShortDescription(), "is not healthy, but will be checked!")
		health.failures = 0
		health.lastCheck = time.Now()
		return true
	}

	return false
}
예제 #4
0
func (mr *MultiRunner) getHealth(runner *common.RunnerConfig) *RunnerHealth {
	mr.healthyLock.Lock()
	defer mr.healthyLock.Unlock()

	if mr.healthy == nil {
		mr.healthy = map[string]*RunnerHealth{}
	}
	health := mr.healthy[runner.UniqueID()]
	if health == nil {
		health = &RunnerHealth{
			lastCheck: time.Now(),
		}
		mr.healthy[runner.UniqueID()] = health
	}
	return health
}
func (s *RegistrationContext) askDocker(runnerConfig *common.RunnerConfig) {
	dockerConfig := &common.DockerConfig{}
	dockerConfig.Image = s.ask("docker-image", "Please enter the Docker image (eg. ruby:2.1):")
	dockerConfig.Privileged = s.Bool("docker-privileged")

	if s.askForDockerService("mysql", dockerConfig) {
		runnerConfig.Environment = append(runnerConfig.Environment, "MYSQL_ALLOW_EMPTY_PASSWORD=1")
	}

	s.askForDockerService("postgres", dockerConfig)
	s.askForDockerService("redis", dockerConfig)
	s.askForDockerService("mongo", dockerConfig)

	dockerConfig.Volumes = append(dockerConfig.Volumes, "/cache")

	runnerConfig.Docker = dockerConfig
}
예제 #6
0
func (s *RegistrationContext) askSSH(runnerConfig *common.RunnerConfig, serverless bool) {
	runnerConfig.SSH = &ssh.Config{}
	if !serverless {
		if host := s.ask("ssh-host", "Please enter the SSH server address (eg. my.server.com):"); host != "" {
			runnerConfig.SSH.Host = &host
		}
		if port := s.ask("ssh-port", "Please enter the SSH server port (eg. 22):", true); port != "" {
			runnerConfig.SSH.Port = &port
		}
	}
	if user := s.ask("ssh-user", "Please enter the SSH user (eg. root):"); user != "" {
		runnerConfig.SSH.User = &user
	}
	if password := s.ask("ssh-password", "Please enter the SSH password (eg. docker.io):"); password != "" {
		runnerConfig.SSH.Password = &password
	}
}
func (s *RegistrationContext) askSSH(runnerConfig *common.RunnerConfig, serverless bool) {
	runnerConfig.SSH = &ssh.Config{}
	if !serverless {
		if host := s.ask("ssh-host", "Please enter the SSH server address (eg. my.server.com):"); host != "" {
			runnerConfig.SSH.Host = &host
		}
		if port := s.ask("ssh-port", "Please enter the SSH server port (eg. 22):", true); port != "" {
			runnerConfig.SSH.Port = &port
		}
	}
	if user := s.ask("ssh-user", "Please enter the SSH user (eg. root):"); user != "" {
		runnerConfig.SSH.User = &user
	}
	if password := s.ask("ssh-password", "Please enter the SSH password (eg. docker.io):", true); password != "" {
		runnerConfig.SSH.Password = &password
	}
	if identityFile := s.ask("ssh-identity-file", "Please enter path to SSH identity file (eg. /home/user/.ssh/id_rsa):", true); identityFile != "" {
		runnerConfig.SSH.IdentityFile = &identityFile
	}
}
func (s *RegistrationContext) askParallels(runnerConfig *common.RunnerConfig) {
	parallelsConfig := &common.ParallelsConfig{}
	parallelsConfig.BaseName = s.ask("parallels-vm", "Please enter the Parallels VM (eg. my-vm):")
	runnerConfig.Parallels = parallelsConfig
}
func runSingle(c *cli.Context) {
	buildsDir := c.String("builds-dir")
	shell := c.String("shell")
	config := common.NewConfig()
	runner := common.RunnerConfig{
		URL:       c.String("url"),
		Token:     c.String("token"),
		Executor:  c.String("executor"),
		BuildsDir: &buildsDir,
		Shell:     &shell,
	}

	if len(runner.URL) == 0 {
		log.Fatalln("Missing URL")
	}
	if len(runner.Token) == 0 {
		log.Fatalln("Missing Token")
	}
	if len(runner.Executor) == 0 {
		log.Fatalln("Missing Executor")
	}

	go runServer(c.String("addr"))
	go runHerokuURL(c.String("heroku-url"))

	signals := make(chan os.Signal)
	signal.Notify(signals, os.Interrupt, syscall.SIGTERM)

	log.Println("Starting runner for", runner.URL, "with token", runner.ShortDescription(), "...")

	finished := false
	abortSignal := make(chan os.Signal)
	doneSignal := make(chan int, 1)

	go func() {
		interrupt := <-signals
		log.Warningln("Requested exit:", interrupt)
		finished = true

		go func() {
			for {
				abortSignal <- interrupt
			}
		}()

		select {
		case newSignal := <-signals:
			log.Fatalln("forced exit:", newSignal)
		case <-time.After(common.ShutdownTimeout * time.Second):
			log.Fatalln("shutdown timedout")
		case <-doneSignal:
		}
	}()

	for !finished {
		buildData, healthy := common.GetBuild(runner)
		if !healthy {
			log.Println("Runner is not healthy!")
			select {
			case <-time.After(common.NotHealthyCheckInterval * time.Second):
			case <-abortSignal:
			}
			continue
		}

		if buildData == nil {
			select {
			case <-time.After(common.CheckInterval * time.Second):
			case <-abortSignal:
			}
			continue
		}

		newBuild := common.Build{
			GetBuildResponse: *buildData,
			Runner:           &runner,
			BuildAbort:       abortSignal,
		}
		newBuild.AssignID()
		newBuild.Run(config)
	}

	doneSignal <- 0
}