Skip to content

Commit

Permalink
Support Linux (#97)
Browse files Browse the repository at this point in the history
* install-gitlab-runner*.sh: dynamically detect OS and architecture

* Introduce (*VM).Info() method

* Handle builds and cache directories in a cross-platform fashion

* Explain "/var/tmp"
  • Loading branch information
edigaryev authored Dec 23, 2024
1 parent 92a121e commit 7973405
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 29 deletions.
22 changes: 14 additions & 8 deletions internal/commands/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,15 @@ func runConfig(cmd *cobra.Command, args []string) error {
// to be absolute[1].
//
// 2. GitLab Runner uses relative paths internally which results in improper directory traversal[2],
// this is why we use "/private/tmp" instead of just "/tmp" here as a workaround.
// so instead of "/tmp" we need to use "/private/tmp" here as a workaround.
//
// 3. However, there's no "/private/tmp" on Linux. So we use the lowest common denominator
// in the form of "/var/tmp". It's both (1) not a symbolic link and (2) is present on both platforms.
//
// [1]: https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section
// [2]: https://gitlab.com/gitlab-org/gitlab-runner/-/issues/31003
BuildsDir: "/private/tmp/builds",
CacheDir: "/private/tmp/cache",
BuildsDir: "/var/tmp/builds",
CacheDir: "/var/tmp/cache",
JobEnv: map[string]string{},
}

Expand Down Expand Up @@ -95,15 +98,14 @@ func runConfig(cmd *cobra.Command, args []string) error {
// Figure out the builds directory override to use
switch {
case tartConfig.HostDir:
gitlabRunnerConfig.BuildsDir = fmt.Sprintf("/Users/%s/hostdir", tartConfig.SSHUsername)
gitlabRunnerConfig.JobEnv[tart.EnvTartExecutorInternalBuildsDirOnHost] = gitLabEnv.HostDirPath()

if err := os.MkdirAll(gitLabEnv.HostDirPath(), 0700); err != nil {
return err
}
case buildsDir != "":
gitlabRunnerConfig.BuildsDir = fmt.Sprintf("/Users/%s/buildsdir", tartConfig.SSHUsername)
buildsDir = os.ExpandEnv(buildsDir)
gitlabRunnerConfig.JobEnv[tart.EnvTartExecutorInternalBuildsDir] = buildsDir
gitlabRunnerConfig.JobEnv[tart.EnvTartExecutorInternalBuildsDirOnHost] = buildsDir

if err := os.MkdirAll(buildsDir, 0700); err != nil {
return err
Expand All @@ -115,9 +117,8 @@ func runConfig(cmd *cobra.Command, args []string) error {
// Figure out the cache directory override to use
switch {
case cacheDir != "":
gitlabRunnerConfig.CacheDir = fmt.Sprintf("/Users/%s/cachedir", tartConfig.SSHUsername)
cacheDir = os.ExpandEnv(cacheDir)
gitlabRunnerConfig.JobEnv[tart.EnvTartExecutorInternalCacheDir] = cacheDir
gitlabRunnerConfig.JobEnv[tart.EnvTartExecutorInternalCacheDirOnHost] = cacheDir

if err := os.MkdirAll(cacheDir, 0700); err != nil {
return err
Expand All @@ -126,6 +127,11 @@ func runConfig(cmd *cobra.Command, args []string) error {
gitlabRunnerConfig.CacheDir = guestCacheDir
}

// Propagate builds and cache directory locations in the guest
// because GitLab Runner won't do this for us
gitlabRunnerConfig.JobEnv[tart.EnvTartExecutorInternalBuildsDir] = gitlabRunnerConfig.BuildsDir
gitlabRunnerConfig.JobEnv[tart.EnvTartExecutorInternalCacheDir] = gitlabRunnerConfig.CacheDir

jsonBytes, err := json.MarshalIndent(&gitlabRunnerConfig, "", " ")
if err != nil {
return err
Expand Down
13 changes: 12 additions & 1 deletion internal/commands/prepare/install-gitlab-runner-auto.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,18 @@
#
set -euo pipefail

GITLAB_RUNNER_URL="https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-arm64"
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case $ARCH in
aarch64)
ARCH="arm64"
;;
x86_64)
ARCH="amd64"
;;
esac

GITLAB_RUNNER_URL="https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-${OS}-${ARCH}"
GITLAB_RUNNER_PATH="/usr/local/bin/gitlab-runner"

# Is GitLab Runner already installed?
Expand Down
13 changes: 12 additions & 1 deletion internal/commands/prepare/install-gitlab-runner-curl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,19 @@
#
set -euo pipefail

OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case $ARCH in
aarch64)
ARCH="arm64"
;;
x86_64)
ARCH="amd64"
;;
esac

GITLAB_RUNNER_VERSION="latest"
GITLAB_RUNNER_URL="https://gitlab-runner-downloads.s3.amazonaws.com/${GITLAB_RUNNER_VERSION}/binaries/gitlab-runner-darwin-arm64"
GITLAB_RUNNER_URL="https://gitlab-runner-downloads.s3.amazonaws.com/${GITLAB_RUNNER_VERSION}/binaries/gitlab-runner-${OS}-${ARCH}"
GITLAB_RUNNER_PATH="/usr/local/bin/gitlab-runner"

# Is GitLab Runner already installed?
Expand Down
46 changes: 34 additions & 12 deletions internal/commands/prepare/prepare.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,29 +191,51 @@ func runPrepareVM(cmd *cobra.Command, args []string) error {
log.Printf("Timezone was set to %s!\n", tz)
}

dirsToMount := []string{}
if config.HostDir {
dirsToMount = append(dirsToMount, "hostdir")
type MountPoint struct {
Name string
Path string
}
if _, ok := os.LookupEnv(tart.EnvTartExecutorInternalBuildsDir); ok {
dirsToMount = append(dirsToMount, "buildsdir")

vmInfo, err := vm.Info(cmd.Context())
if err != nil {
return err
}
if _, ok := os.LookupEnv(tart.EnvTartExecutorInternalCacheDir); ok {
dirsToMount = append(dirsToMount, "cachedir")

var mountPoints []MountPoint

if _, ok := os.LookupEnv(tart.EnvTartExecutorInternalBuildsDirOnHost); ok {
mountPoints = append(mountPoints, MountPoint{
Name: "buildsdir",
Path: os.Getenv(tart.EnvTartExecutorInternalBuildsDir),
})
}
if _, ok := os.LookupEnv(tart.EnvTartExecutorInternalCacheDirOnHost); ok {
mountPoints = append(mountPoints, MountPoint{
Name: "cachedir",
Path: os.Getenv(tart.EnvTartExecutorInternalCacheDir),
})
}

for _, dirToMount := range dirsToMount {
log.Printf("Mounting %s...\n", dirToMount)
for _, mountPoint := range mountPoints {
log.Printf("Mounting %s on %s...\n", mountPoint.Name, mountPoint.Path)

session, err := ssh.NewSession()
if err != nil {
return err
}
defer session.Close()

mountPoint := fmt.Sprintf("/Users/%s/%s", config.SSHUsername, dirToMount)
mkdirScript := fmt.Sprintf("mkdir -p %s", mountPoint)
mountScript := fmt.Sprintf("mount_virtiofs tart.virtiofs.%s.%s %s", dirToMount, gitLabEnv.JobID, mountPoint)
var command string

if vmInfo.OS == "darwin" {
command = "mount_virtiofs"
} else {
command = "sudo mount -t virtiofs"
}

mkdirScript := fmt.Sprintf("mkdir -p %s", mountPoint.Path)
mountScript := fmt.Sprintf("%s tart.virtiofs.%s.%s %s", command, mountPoint.Name,
gitLabEnv.JobID, mountPoint.Path)
session.Stdin = bytes.NewBufferString(strings.Join([]string{mkdirScript, mountScript, ""}, "\n"))
session.Stdout = os.Stdout
session.Stderr = os.Stderr
Expand Down
10 changes: 10 additions & 0 deletions internal/tart/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,20 @@ const (
// by the user.
EnvTartExecutorInternalBuildsDir = "TART_EXECUTOR_INTERNAL_BUILDS_DIR"

// EnvTartExecutorInternalBuildsDirOnHost is an internal environment variable
// that does not use the "CUSTOM_ENV_" prefix, thus preventing the override
// by the user.
EnvTartExecutorInternalBuildsDirOnHost = "TART_EXECUTOR_INTERNAL_BUILDS_DIR_ON_HOST"

// EnvTartExecutorInternalCacheDir is an internal environment variable
// that does not use the "CUSTOM_ENV_" prefix, thus preventing the override
// by the user.
EnvTartExecutorInternalCacheDir = "TART_EXECUTOR_INTERNAL_CACHE_DIR"

// EnvTartExecutorInternalCacheDirOnHost is an internal environment variable
// that does not use the "CUSTOM_ENV_" prefix, thus preventing the override
// by the user.
EnvTartExecutorInternalCacheDirOnHost = "TART_EXECUTOR_INTERNAL_CACHE_DIR_ON_HOST"
)

type Config struct {
Expand Down
33 changes: 26 additions & 7 deletions internal/tart/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tart
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -35,6 +36,10 @@ type VM struct {
id string
}

type VMInfo struct {
OS string `json:"os"`
}

func ExistingVM(gitLabEnv gitlab.Env) *VM {
return &VM{
id: gitLabEnv.VirtualMachineID(),
Expand Down Expand Up @@ -143,15 +148,14 @@ func (vm *VM) Start(
runArgs = append(runArgs, "--disk", customDiskMount)
}

if config.HostDir {
hostDir := gitLabEnv.HostDirPath()
runArgs = append(runArgs, "--dir", fmt.Sprintf("%s:tag=tart.virtiofs.hostdir.%s", hostDir, gitLabEnv.JobID))
} else if buildsDir, ok := os.LookupEnv(EnvTartExecutorInternalBuildsDir); ok {
runArgs = append(runArgs, "--dir", fmt.Sprintf("%s:tag=tart.virtiofs.buildsdir.%s", buildsDir, gitLabEnv.JobID))
if buildsDir, ok := os.LookupEnv(EnvTartExecutorInternalBuildsDirOnHost); ok {
runArgs = append(runArgs, "--dir", fmt.Sprintf("%s:tag=tart.virtiofs.buildsdir.%s",
buildsDir, gitLabEnv.JobID))
}

if cacheDir, ok := os.LookupEnv(EnvTartExecutorInternalCacheDir); ok {
runArgs = append(runArgs, "--dir", fmt.Sprintf("%s:tag=tart.virtiofs.cachedir.%s", cacheDir, gitLabEnv.JobID))
if cacheDir, ok := os.LookupEnv(EnvTartExecutorInternalCacheDirOnHost); ok {
runArgs = append(runArgs, "--dir", fmt.Sprintf("%s:tag=tart.virtiofs.cachedir.%s",
cacheDir, gitLabEnv.JobID))
}

runArgs = append(runArgs, vm.id)
Expand Down Expand Up @@ -278,6 +282,21 @@ func (vm *VM) IP(ctx context.Context, config Config) (string, error) {
return strings.TrimSpace(stdout), nil
}

func (vm *VM) Info(ctx context.Context) (*VMInfo, error) {
stdout, _, err := TartExec(ctx, "get", "--format", "json", vm.id)
if err != nil {
return nil, err
}

var vmInfo VMInfo

if err := json.Unmarshal([]byte(stdout), &vmInfo); err != nil {
return nil, err
}

return &vmInfo, nil
}

func (vm *VM) Stop() error {
_, _, err := TartExec(context.Background(), "stop", vm.id)

Expand Down

0 comments on commit 7973405

Please sign in to comment.