Skip to content
This repository has been archived by the owner on Feb 15, 2023. It is now read-only.

Commit

Permalink
run snake-runner in docker
Browse files Browse the repository at this point in the history
  • Loading branch information
kovetskiy committed Jan 17, 2020
1 parent e0b8ff3 commit 10eb896
Show file tree
Hide file tree
Showing 16 changed files with 310 additions and 39 deletions.
Empty file removed Dockerfile
Empty file.
17 changes: 14 additions & 3 deletions Taskfile.yml
Expand Up @@ -11,14 +11,25 @@ vars:

includes:
go: tasks/go.yml
docker: tasks/docker.yml

tasks:
rr:
cmds:
- task: reset
- task: build
- task: go:build
- mkdir -p .runners/{{ .random_uuid }}
- SNAKE_NAME={{ .random_uuid }}
SNAKE_TOKEN_PATH=.runners/{{.random_uuid}}/token
SNAKE_SSH_KEY_PATH=.runners/{{.random_uuid}}/id_rsa
./snake-runner -c ./conf/snake.dev.conf
./snake-runner -c ./conf/dev.conf
dock:
deps: [docker:alpine]
cmds:
- docker run -it
-e SNAKE_MASTER_ADDRESS=6.2.0.bitbucket:7990
-e SNAKE_DOCKER_NETWORK=bitbucket
-v /var/lib/snake-runner/secrets:{{.pwd}}/secrets
-v /var/run/docker.sock:/var/run/docker.sock
--rm
--network bitbucket
snake-runner
5 changes: 5 additions & 0 deletions cmd/snake-runner/main.go
Expand Up @@ -5,6 +5,7 @@ import (
"syscall"

"github.com/docopt/docopt-go"
"github.com/reconquest/karma-go"
"github.com/reconquest/pkg/log"
"github.com/reconquest/sign-go"
)
Expand Down Expand Up @@ -42,6 +43,8 @@ func main() {
log.Fatal(err)
}

log.Infof(karma.Describe("version", version), "starting snake-runner")

config, err := LoadRunnerConfig(options.ConfigPathValue)
if err != nil {
log.Fatal(err)
Expand All @@ -55,6 +58,8 @@ func main() {
log.SetLevel(log.LevelTrace)
}

log.Infof(nil, "runner name: %s", config.Name)

runner := NewRunner(config)
runner.Start()

Expand Down
18 changes: 1 addition & 17 deletions cmd/snake-runner/runner.go
Expand Up @@ -2,7 +2,6 @@ package main

import (
"net/http"
"os"
"strings"
"time"

Expand All @@ -21,27 +20,12 @@ type Runner struct {
client *http.Client
config *RunnerConfig
scheduler *Scheduler

name string
}

func NewRunner(config *RunnerConfig) *Runner {
name := config.Name
if name == "" {
hostname, err := os.Hostname()
if err != nil {
panic(err)
}

name = hostname
}

log.Infof(nil, "starting runner: %s", name)

return &Runner{
config: config,
client: http.DefaultClient,
name: name,
}
}

Expand Down Expand Up @@ -88,7 +72,7 @@ func (runner *Runner) request() *Request {
BaseURL(master+MasterPrefixAPI).
UserAgent("snake-runner/"+version).
// required by bitbucket itself
Header(NameHeader, runner.name).
Header(NameHeader, runner.config.Name).
Header(TokenHeader, runner.config.Token).
Header("X-Atlassian-Token", "no-check")
}
106 changes: 93 additions & 13 deletions cmd/snake-runner/runner_config.go
@@ -1,24 +1,29 @@
package main

import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"

"gopkg.in/yaml.v2"

"github.com/kovetskiy/ko"
"github.com/reconquest/executil-go"
"github.com/reconquest/karma-go"
"github.com/reconquest/pkg/log"
"github.com/reconquest/snake-runner/internal/sshkey"
)

const (
defaultBlockSize = 4096
)

type RunnerConfig struct {
ListenAddress string `yaml:"listen_address" env:"SNAKE_LISTEN_ADDRESS" required:"true" default:":8585" `
MasterAddress string `yaml:"master_address" env:"SNAKE_MASTER_ADDRESS" required:"true"`
// it's actually required but it will be handled manually
MasterAddress string `yaml:"master_address" env:"SNAKE_MASTER_ADDRESS"`

Log struct {
Debug bool `yaml:"debug" env:"SNAKE_LOG_DEBUG"`
Expand All @@ -28,15 +33,17 @@ type RunnerConfig struct {
Name string `yaml:"name" env:"SNAKE_NAME"`

Token string `yaml:"token" env:"SNAKE_TOKEN"`
TokenPath string `yaml:"token_path" env:"SNAKE_TOKEN_PATH" default:"/etc/snake-runner/token"`
TokenPath string `yaml:"token_path" env:"SNAKE_TOKEN_PATH" default:"/var/lib/snake-runner/secrets/token"`

HeartbeatInterval time.Duration `yaml:"heartbeat_interval" default:"30s"`
SchedulerInterval time.Duration `yaml:"scheduler_interval" default:"5s"`

Virtualization string `yaml:"virtualization" default:"docker" required:"true"`
MaxParallelPipelines int64 `yaml:"max_parallel_pipelines" default:"0" required:"true"`
Virtualization string `yaml:"virtualization" default:"docker" env:"SNAKE_VIRTUALIZATION" required:"true"`
MaxParallelPipelines int64 `yaml:"max_parallel_pipelines" env:"SNAKE_MAX_PARALLEL_PIPELINES" default:"0" required:"true"`

SSHKey string `yaml:"ssh_key" env:"SNAKE_SSH_KEY_PATH" default:"/etc/snake-runner/id_rsa" required:"true"`
SSHKey string `yaml:"ssh_key" env:"SNAKE_SSH_KEY_PATH" default:"/var/lib/snake-runner/secrets/id_rsa" required:"true"`

DockerNetwork string `yaml:"docker_network" env:"SNAKE_DOCKER_NETWORK"`
}

func LoadRunnerConfig(path string) (*RunnerConfig, error) {
Expand All @@ -48,6 +55,11 @@ func LoadRunnerConfig(path string) (*RunnerConfig, error) {
return nil, err
}

if config.MasterAddress == "" {
showMessageNoConfiguration()
os.Exit(1)
}

if config.TokenPath != "" && config.Token == "" {
tokenData, err := ioutil.ReadFile(config.TokenPath)
if err != nil && !os.IsNotExist(err) {
Expand All @@ -66,11 +78,23 @@ func LoadRunnerConfig(path string) (*RunnerConfig, error) {
}

if !isFileExists(config.SSHKey) && !isFileExists(config.SSHKey+".pub") {
log.Warningf(nil, "ssh key not found at %s, generating it", config.SSHKey)

_, _, err := executil.Run(
exec.Command("ssh-keygen", "-t", "rsa", "-C", "snake-runner", "-f", config.SSHKey),
log.Warningf(
nil,
"SSH key not found, generating it (block size: %d): %s",
defaultBlockSize,
config.SSHKey,
)

dir := filepath.Base(config.SSHKey)
err = os.MkdirAll(dir, 0644)
if err != nil {
return nil, karma.Format(
err,
"unable to make directory for ssh key: %s", dir,
)
}

err = sshkey.GeneratePair(config.SSHKey, defaultBlockSize)
if err != nil {
return nil, karma.Format(
err,
Expand All @@ -84,15 +108,71 @@ func LoadRunnerConfig(path string) (*RunnerConfig, error) {

log.Warningf(
nil,
"max_parallel_pipelines is not specified, number of cpu will be used instead: %d",
"max_parallel_pipelines is not specified, number of CPU will be used instead: %d",
config.MaxParallelPipelines,
)
}

if config.Name == "" {
hostname, err := os.Hostname()
if err != nil {
return nil, karma.Format(err, "unable to obtain hostname")
}

config.Name = hostname
}

return &config, nil
}

func isFileExists(path string) bool {
stat, err := os.Stat(path)
return !os.IsNotExist(err) && !stat.IsDir()
}

func showMessageNoConfiguration() {
fmt.Fprintln(os.Stderr, `
The snake-runner is ready to start but you have not provided address
where you run Bitbucket server with Snake CI addon installed.
There are two ways to fix it:
1) specify master_address in your config file, by default it is /etc/snake-runner/snake-runner.conf
2) specify address using the environment variable SNAKE_MASTER_ADDRESS, like as following:
* SNAKE_MASTER_ADDRESS=http://mybitbucket.company/ snake-runner
or if you are running snake-runner in docker:
* docker run -e SNAKE_MASTER_ADDRESS=http://mybitbucket.company/ <other-docker-flags-here>
See also: <XXXXXXXXX>`)
}

func isDocker() bool {
contents, err := ioutil.ReadFile("/proc/1/cgroup")
if err != nil {
log.Errorf(err, "unable to read /proc/1/cgroup to determine "+
"is it docker container or not")
}

/**
* A docker container has /docker/ in its /cgroup file
*
* / # cat /proc/1/cgroup | grep docker
* 11:pids:/docker/14f3db3a669169c0b801a3ac99...
* 10:freezer:/docker/14f3db3a669169c0b801a3ac9...
* 9:cpu,cpuacct:/docker/14f3db3a669169c0b801a3ac...
* 8:hugetlb:/docker/14f3db3a669169c0b801a3ac99f89e...
* 7:perf_event:/docker/14f3db3a669169c0b801a3...
* 6:devices:/docker/14f3db3a669169c0b801a3ac99f...
* 5:memory:/docker/14f3db3a669169c0b801a3ac99f89e...
* 4:blkio:/docker/14f3db3a669169c0b801a3ac99f89e914...
* 3:cpuset:/docker/14f3db3a669169c0b801a3ac99f89e914a...
* 2:net_cls,net_prio:/docker/14f3db3a669169c0b801a3ac...
* 1:name=systemd:/docker/14f3db3a669169c0b801a3ac99f89e...
* 0::/system.slice/docker.service
***/
if strings.Contains(string(contents), "/docker/") {
return true
}

return false
}
2 changes: 1 addition & 1 deletion cmd/snake-runner/runner_register.go
Expand Up @@ -44,7 +44,7 @@ func (runner *Runner) register() (string, error) {
}

request := requests.RunnerRegister{
Name: runner.name,
Name: runner.config.Name,
PublicKey: strings.TrimSpace(string(publicKey)),
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/snake-runner/scheduler.go
Expand Up @@ -14,7 +14,7 @@ import (
)

func (runner *Runner) startScheduler() error {
docker, err := cloud.NewDocker()
docker, err := cloud.NewDocker(runner.config.DockerNetwork)
if err != nil {
return karma.Format(err, "unable to initialize container provider")
}
Expand Down
30 changes: 30 additions & 0 deletions conf/snake-runner.conf
@@ -0,0 +1,30 @@
# address of bitbucket server with Snake CI plugin installed
# master_address: ""

# log:
# debug: false
# trace: false

# a custom name of runner, hostname is used as default
# name: ""

# specify custom runner token. you should never add it unless you are moving
# your runner to another location and want to save it's id
# token: ""

# path to use to save token retrieved by master during registration
# token_path: "/etc/snake-runner/token"

# how often should runner let master know that it's online
# heartbeat_interval: "30s"
#
# how often should runner ask for a job
# heartbeat_interval: "5s"
#
# how many parallel pipelines can be running, 0 means to use number of CPUs
# max_parallel_pipelines: 0
#
# path to ssh key that will be used for retrieving git repositories. it's not
# required because snake-runner can generate it and send public part to Snake
# CI plugin to add to repositories during job execution
# ssh_key: "/etc/snake-runner/id_rsa"
23 changes: 23 additions & 0 deletions docker/alpine.dockerfile
@@ -0,0 +1,23 @@
FROM alpine:3

COPY /snake-runner.docker /bin/snake-runner

RUN mkdir /etc/snake-runner/
COPY /conf/snake-runner.conf /etc/snake-runner/snake-runner.conf

ENV SNAKE_MASTER_ADDRESS ""
ENV SNAKE_LOG_DEBUG ""
ENV SNAKE_LOG_TRACE ""
ENV SNAKE_NAME ""
ENV SNAKE_TOKEN ""
ENV SNAKE_TOKEN_PATH ""
ENV SNAKE_VIRTUALIZATION ""
ENV SNAKE_MAX_PARALLEL_PIPELINES ""
ENV SNAKE_SSH_KEY_PATH ""

VOLUME /var/run/docker.sock
VOLUME /var/lib/snake-runner/secrets/

CMD ["/bin/snake-runner"]

# vim: ft=dockerfile
3 changes: 3 additions & 0 deletions go.mod
Expand Up @@ -18,6 +18,7 @@ require (
github.com/kovetskiy/ko v0.0.0-20200107130756-5aeb3d88d1de
github.com/kovetskiy/lorg v0.0.0-20200107130803-9a7136a95634 // indirect
github.com/kovetskiy/toml v0.2.0 // indirect
github.com/moby/moby v1.13.1
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
Expand All @@ -30,7 +31,9 @@ require (
github.com/stretchr/testify v1.4.0
github.com/vmware/govmomi v0.22.1
github.com/zazab/zhash v0.0.0-20170403032415-ad45b89afe7a // indirect
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 // indirect
gonum.org/v1/gonum v0.6.2
google.golang.org/api v0.15.0 // indirect
google.golang.org/grpc v1.26.0 // indirect
gopkg.in/yaml.v2 v2.2.7
Expand Down

0 comments on commit 10eb896

Please sign in to comment.