garm/util/util.go
Gabriel Adrian Samfira ebec0dda52 Handle org runners
2022-04-22 14:46:27 +00:00

160 lines
4.2 KiB
Go

package util
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
"github.com/google/go-github/v43/github"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
"golang.org/x/oauth2"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
"runner-manager/cloudconfig"
"runner-manager/config"
runnerErrors "runner-manager/errors"
"runner-manager/params"
)
var (
OSToOSTypeMap map[string]config.OSType = map[string]config.OSType{
"ubuntu": config.Linux,
"rhel": config.Linux,
"centos": config.Linux,
"suse": config.Linux,
"fedora": config.Linux,
"flatcar": config.Linux,
"gentoo": config.Linux,
"windows": config.Windows,
}
)
// GetLoggingWriter returns a new io.Writer suitable for logging.
func GetLoggingWriter(cfg *config.Config) (io.Writer, error) {
var writer io.Writer = os.Stdout
if cfg.LogFile != "" {
dirname := path.Dir(cfg.LogFile)
if _, err := os.Stat(dirname); err != nil {
if !os.IsNotExist(err) {
return nil, fmt.Errorf("failed to create log folder")
}
if err := os.MkdirAll(dirname, 0o711); err != nil {
return nil, fmt.Errorf("failed to create log folder")
}
}
writer = &lumberjack.Logger{
Filename: cfg.LogFile,
MaxSize: 500, // megabytes
MaxBackups: 3,
MaxAge: 28, //days
Compress: true, // disabled by default
}
}
return writer, nil
}
func FindRunnerType(runnerType string, runners []config.Runner) (config.Runner, error) {
for _, runner := range runners {
if runner.Name == runnerType {
return runner, nil
}
}
return config.Runner{}, runnerErrors.ErrNotFound
}
func ConvertFileToBase64(file string) (string, error) {
bytes, err := ioutil.ReadFile(file)
if err != nil {
return "", errors.Wrap(err, "reading file")
}
return base64.StdEncoding.EncodeToString(bytes), nil
}
// GenerateSSHKeyPair generates a private/public key-pair.
// Shamlessly copied from: https://stackoverflow.com/questions/21151714/go-generate-an-ssh-public-key
func GenerateSSHKeyPair() (pubKey, privKey []byte, err error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, nil, err
}
// generate and write private key as PEM
var privKeyBuf bytes.Buffer
privateKeyPEM := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}
if err := pem.Encode(&privKeyBuf, privateKeyPEM); err != nil {
return nil, nil, err
}
// generate and write public key
pub, err := ssh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, nil, err
}
return ssh.MarshalAuthorizedKey(pub), privKeyBuf.Bytes(), nil
}
func OSToOSType(os string) (config.OSType, error) {
osType, ok := OSToOSTypeMap[strings.ToLower(os)]
if !ok {
return config.Unknown, fmt.Errorf("no OS to OS type mapping for %s", os)
}
return osType, nil
}
func GithubClientFromConfig(ctx context.Context, cfg config.Github) (*github.Client, error) {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: cfg.OAuth2Token},
)
tc := oauth2.NewClient(ctx, ts)
ghClient := github.NewClient(tc)
return ghClient, nil
}
func GetCloudConfig(bootstrapParams params.BootstrapInstance, tools github.RunnerApplicationDownload, runnerName string) (string, error) {
cloudCfg := cloudconfig.NewDefaultCloudInitConfig()
installRunnerParams := cloudconfig.InstallRunnerParams{
FileName: *tools.Filename,
DownloadURL: *tools.DownloadURL,
GithubToken: bootstrapParams.GithubRunnerAccessToken,
RunnerUsername: config.DefaultUser,
RunnerGroup: config.DefaultUser,
RepoURL: bootstrapParams.RepoURL,
RunnerName: runnerName,
RunnerLabels: strings.Join(bootstrapParams.Labels, ","),
}
installScript, err := cloudconfig.InstallRunnerScript(installRunnerParams)
if err != nil {
return "", errors.Wrap(err, "generating script")
}
cloudCfg.AddSSHKey(bootstrapParams.SSHKeys...)
cloudCfg.AddFile(installScript, "/install_runner.sh", "root:root", "755")
cloudCfg.AddRunCmd("/install_runner.sh")
cloudCfg.AddRunCmd("rm -f /install_runner.sh")
asStr, err := cloudCfg.Serialize()
if err != nil {
return "", errors.Wrap(err, "creating cloud config")
}
return asStr, nil
}