Commit 0a7cbc09 authored by Kamil Trzciński's avatar Kamil Trzciński

Initial commit

parents
*.iml
config.toml
package main
type Build struct {
GetBuildResponse
}
func (b *Build) Run() {
}
package main
type RunnerConfig struct {
Name string `json:"name"`
URL string `json:"url"`
Token string `json:"token"`
Limit int `json:"limit"`
}
type Config struct {
Concurrent int `json:"concurrent"`
Runners []RunnerConfig `json:"runners"`
}
package main
import (
"github.com/codegangsta/cli"
)
var (
flConfigFile = cli.StringFlag{
Name: "config",
Value: "config.toml",
Usage: "Config file",
EnvVar: "CONFIG_FILE",
}
flURL = cli.StringFlag{
Name: "URL",
Value: "",
Usage: "Runner URL",
EnvVar: "RUNNER_URL",
}
flToken = cli.StringFlag{
Name: "token",
Value: "",
Usage: "Runner token",
EnvVar: "RUNNER_TOKEN",
}
flRegistrationToken = cli.StringFlag{
Name: "registration-token",
Value: "",
Usage: "Runner's registration token",
EnvVar: "REGISTRATION_TOKEN",
}
flHostname = cli.StringFlag{
Name: "hostname",
Value: "",
Usage: "Runner's registration hostname",
EnvVar: "HOSTNAME",
}
)
package main
import (
"os"
"path"
"fmt"
"github.com/codegangsta/cli"
log "github.com/Sirupsen/logrus"
)
func main() {
app := cli.NewApp()
app.Name = path.Base(os.Args[0])
app.Usage = "a GitLab-CI Multi Runner"
app.Version = "0.1.0"
app.Author = ""
app.Email = ""
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "debug",
Usage: "debug mode",
EnvVar: "DEBUG",
},
cli.StringFlag{
Name: "log-level, l",
Value: "info",
Usage: fmt.Sprintf("Log level (options: debug, info, warn, error, fatal, panic)"),
},
}
// logs
app.Before = func(c *cli.Context) error {
log.SetOutput(os.Stderr)
level, err := log.ParseLevel(c.String("log-level"))
if err != nil {
log.Fatalf(err.Error())
}
log.SetLevel(level)
// If a log level wasn't specified and we are running in debug mode,
// enforce log-level=debug.
if !c.IsSet("log-level") && !c.IsSet("l") && c.Bool("debug") {
log.SetLevel(log.DebugLevel)
}
return nil
}
app.Commands = []cli.Command{
{
Name: "run",
ShortName: "r",
Usage: "start single runner",
Flags: []cli.Flag{flToken, flURL},
Action: run,
},
{
Name: "setup",
ShortName: "s",
Usage: "setup a new runner",
Flags: []cli.Flag{flRegistrationToken, flURL, flHostname},
Action: setup,
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
package main
import (
"encoding/json"
"io/ioutil"
"io"
"errors"
"net/http"
"bytes"
"fmt"
log "github.com/Sirupsen/logrus"
)
type UpdateState int
const (
UpdateSucceeded UpdateState = iota
UpdateAbort
UpdateFailed
)
type BuildState string
const (
Pending BuildState = "pending"
Running = "running"
Failed = "failed"
Success = "success"
)
type GetBuildRequest struct {
Token string `json:"token,omitempty"`
}
type GetBuildResponse struct {
Id int `json:"token,omitempty"`
ProjectId int `json:"project_id,omitempty"`
Commands string `json:"commands,omitempty"`
RepoURL string `json:"repo_url,omitempty"`
Sha string `json:"sha,omitempty"`
RefName string `json:"ref,omitempty"`
BeforeSha string `json:"before_sha,omitempty"`
AllowGitFetch bool `json:"allow_git_fetch,omitempty"`
Timeout int `json:"timeout,omitempty"`
}
type RegisterRunnerRequest struct {
Token string `json:"token,omitempty"`
Hostname string `json:"hostname,omitempty"`
}
type RegisterRunnerResponse struct {
Token string `json:"token,omitempty"`
}
type UpdateBuildRequest struct {
Token string `json:"token,omitempty"`
State BuildState `json:"state,omitempty"`
Trace string `json:"trace,omitempty"`
}
func sendJsonRequest(url string, method string, statusCode int, request interface{}, response interface{}) int {
var data *bytes.Reader
if request != nil {
body, err := json.Marshal(request)
if err != nil {
log.Errorf("Failed to marshal project object: %v", err)
return -1
}
data = bytes.NewReader(body)
}
req, err := http.NewRequest(method, url, data)
if err != nil {
log.Errorf("Failed to create NewRequest", err)
return -1
}
if request != nil {
req.Header.Set("Content-Type", "application/json")
}
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Errorf("Couldn't execute %v against %s: %v", req.Method, req.URL, err)
return -1
}
defer res.Body.Close()
if res.StatusCode == statusCode {
if response != nil {
d := json.NewDecoder(res.Body)
err = d.Decode(response)
if err != nil {
log.Errorf("Error decoding json payload %v", err)
return -1
}
}
}
return res.StatusCode
}
func getJson(url string, statusCode int, response interface{}) int {
return sendJsonRequest(url, "GET", statusCode, nil, response)
}
func postJson(url string, statusCode int, request interface{}, response interface{}) int {
return sendJsonRequest(url, "POST", statusCode, request, response)
}
func putJson(url string, statusCode int, request interface{}, response interface{}) int {
return sendJsonRequest(url, "PUT", statusCode, request, response)
}
func readPayload(r io.Reader) ([]byte, error) {
maxPayloadSize := int64(1 << 63 - 1)
maxPayloadSize = int64(10 << 20) // 10 MB is a lot of text.
b, err := ioutil.ReadAll(io.LimitReader(r, maxPayloadSize+1))
if err != nil {
return nil, err
}
if int64(len(b)) > maxPayloadSize {
err = errors.New("http: POST too large")
return nil, err
}
return b, nil
}
func getUrl(baseURL string, request string, a ...interface{}) string {
return fmt.Sprintf("%s/api/v1/%s", baseURL, fmt.Sprintf(request, a...));
}
func GetBuild(config *RunnerConfig) *GetBuildResponse {
request := GetBuildRequest{
Token: config.Token,
}
var response GetBuildResponse
result := postJson(getUrl(config.URL, "builds/register.json"), 201, &request, &response)
switch result {
case 201: return &response
case 403: return nil
default: return nil
}
}
func RegisterRunner(config *RunnerConfig) *RegisterRunnerResponse {
request := RegisterRunnerRequest{
Token: config.Token,
Hostname: config.Name,
}
var response RegisterRunnerResponse
result := postJson(getUrl(config.URL, "runners/register.json"), 201, &request, &response)
switch result {
case 201: return &response
default: return nil
}
}
func UpdateBuild(config *RunnerConfig, id int, state BuildState, trace io.Reader) UpdateState {
data, err := readPayload(trace)
if err != nil {
return UpdateFailed
}
request := UpdateBuildRequest{
Token: config.Token,
State: state,
Trace: string(data),
}
result := putJson(getUrl(config.URL, "builds/%d.json", id), 201, &request, nil)
switch result {
case 201: return UpdateSucceeded
case 404: return UpdateAbort
default: return UpdateFailed
}
}
package main
import (
"time"
"github.com/codegangsta/cli"
)
func run(c *cli.Context) {
runner_config := RunnerConfig{
URL: flURL.Value,
Token: flToken.Value,
}
for {
new_build := GetBuild(&runner_config)
if new_build != nil {
time.Sleep(3 * time.Second)
continue
}
build := Build{new_build}
build.Run()
}
}
package main
import (
"log"
"bytes"
"os"
"bufio"
"strings"
"io/ioutil"
"github.com/codegangsta/cli"
"github.com/BurntSushi/toml"
)
func setup(c *cli.Context) {
log.SetFlags(0)
config := Config{
Concurrent: 1,
}
if _, err := os.Stat(flConfigFile.Value); err == nil {
if _, err := toml.DecodeFile(flConfigFile.Value, &config); err != nil {
panic(err)
}
}
runner_config := RunnerConfig{
URL: flURL.Value,
Token: flRegistrationToken.Value,
Limit: 1,
}
bio := bufio.NewReader(os.Stdin)
for len(runner_config.URL) == 0 {
log.Printf("Please enter the gitlab-ci coordinator URL (e.g. http://gitlab-ci.org:3000/ )")
data, _, err := bio.ReadLine()
if err != nil {
panic(err)
}
runner_config.URL = string(data)
runner_config.URL = strings.TrimSpace(runner_config.URL)
}
for len(runner_config.Name) == 0 {
log.Printf("Please enter the gitlab-ci hostname for this runner:")
data, _, err := bio.ReadLine()
if err != nil {
panic(err)
}
runner_config.Name = string(data)
runner_config.Name = strings.TrimSpace(runner_config.Name)
}
for len(runner_config.Token) == 0 {
log.Printf("Please enter the gitlab-ci token for this runner:")
data, _, err := bio.ReadLine()
if err != nil {
panic(err)
}
runner_config.Token = string(data)
runner_config.Token = strings.TrimSpace(runner_config.Token)
}
result := RegisterRunner(&runner_config)
if result == nil {
log.Fatalf("Failed to register this runner. Perhaps your SSH key is invalid or you are having network problems");
}
config.Runners = append(config.Runners, runner_config)
var new_config bytes.Buffer
new_buffer := bufio.NewWriter(&new_config)
if err := toml.NewEncoder(new_buffer).Encode(&config); err != nil {
log.Fatalf("Error encoding TOML: %s", err)
}
if err := new_buffer.Flush(); err != nil {
panic(err)
}
if err := ioutil.WriteFile(flConfigFile.Value, new_config.Bytes(), 0600); err != nil {
panic(err)
}
log.Printf("Runner registered successfully. Feel free to start it!")
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment