jynx/backend/hetzner/servers.go
Nils Stinnesbeck f359f0fb1f
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/tag/woodpecker Pipeline was successful
made server more dynamic via environment variable, fixed negative
starting up servers
2023-07-15 23:31:23 +02:00

186 lines
4.8 KiB
Go

package hetzner
import (
"context"
"fmt"
"os"
"regexp"
"strconv"
"strings"
"time"
"git.stinnesbeck.com/go/log"
"github.com/hetznercloud/hcloud-go/hcloud"
)
func connect() *hcloud.Client {
token := os.Getenv("HCLOUD_API_TOKEN")
if token == "" {
log.PrintError("please specify HCLOUD_API_TOKEN")
os.Exit(1)
}
return hcloud.NewClient(hcloud.WithToken(token))
}
func GetNewSuffix(servers []*hcloud.Server) (int, error) {
// cop out if there are no servers yet
if len(servers) == 0 {
return 1, nil
}
// variable to hold highest number
var highestNumber int
// loop through all servers
for i := range servers {
serverNameSlice := strings.Split(servers[i].Name, "-")
suffix, err := strconv.Atoi(serverNameSlice[len(serverNameSlice)-1])
if err != nil {
return 0, err
}
// compare our current number agains the highest yet
if suffix > highestNumber {
highestNumber = suffix
}
}
// return one number higher than highest number
return highestNumber + 1, nil
}
// ListJynxServers lists all servers managed by Jynx hosted on Hetzner Cloud
func ListJynxServers(prefix string) ([]*hcloud.Server, error) {
// connect to the hcloud API
client := connect()
// get all servers
servers, err := client.Server.All(context.Background())
if err != nil {
return nil, err
}
// create object that holds all servers managed by Jynx
var jynxServers []*hcloud.Server
// loop through servers and filter out managed servers
for _, server := range servers {
if strings.HasPrefix(server.Name, prefix) {
jynxServers = append(jynxServers, server)
}
}
// return managed servers
return jynxServers, nil
}
func CheckServerDeletions(servers []*hcloud.Server) error {
interval := os.Getenv("SCALER_POLL_INTERVAL")
if interval == "" {
interval = "5"
}
pollIntervalSeconds, err := strconv.Atoi(interval)
if err != nil {
return err
}
if pollIntervalSeconds < 60 {
pollIntervalSeconds = 60
}
if pollIntervalSeconds > 3600 {
pollIntervalSeconds %= 3600
}
for _, server := range servers {
safeInterval := time.Duration(3600-pollIntervalSeconds) * time.Second
optimalDeleteTime := server.Created.Add(safeInterval).Local()
log.PrintDebug(server.Name, "optimal delete time:", optimalDeleteTime)
if time.Now().Local().Before(optimalDeleteTime) {
continue
}
// optimal delete time has passed, delete the server
if err := DeleteServer(server); err != nil {
return err
}
}
return nil
}
func DeleteServer(server *hcloud.Server) error {
client := connect()
log.PrintInfo("Deleting", server.Name)
_, response, err := client.Server.DeleteWithResult(context.Background(), server)
if err != nil {
return err
}
if response.StatusCode != 200 {
return fmt.Errorf("api returned errorcode %d", response.StatusCode)
}
return nil
}
func CreateServers(servers []string) error {
// connect to hcloud API
client := connect()
// read in some environment variables
agentSecret := os.Getenv("WOODPECKER_AGENT_SECRET")
r := regexp.MustCompile(`http[s]*:\/\/(.*)`)
hasPort := regexp.MustCompile(`^.*(:\d+)`)
withoutPort := regexp.MustCompile(`^https?:\/\/(.*):`)
woodpeckerHost := r.FindStringSubmatch(os.Getenv("WOODPECKER_HOSTNAME"))[1]
if hasPort.MatchString(woodpeckerHost) {
woodpeckerHost = withoutPort.FindStringSubmatch(woodpeckerHost)[1]
}
// default port for stats
statsPort := "9000"
// check if port was set
if port := os.Getenv("WOODPECKER_STATS_PORT"); port != "" {
statsPort = port
}
woodpeckerHost += ":" + statsPort
// loop through each server
for _, server := range servers {
// create unique AgentCMD for cloud init
AgentCMD := fmt.Sprintf("docker run -d -v /var/run/docker.sock:/var/run/docker.sock -e WOODPECKER_GRPC_SECURE=false -e WOODPECKER_SERVER=%s -e WOODPECKER_AGENT_SECRET=%s -e WOODPECKER_MAX_PROCS=%d -e WOODPECKER_HOSTNAME=%s woodpeckerci/woodpecker-agent", woodpeckerHost, agentSecret, 1, server)
userdata := fmt.Sprintf("#cloud-config\nruncmd:\n - %s", AgentCMD)
labels := make(map[string]string)
labels["created_by"] = "jynx"
labels["created"] = fmt.Sprintf("%d", time.Now().Unix())
var sshkeys []*hcloud.SSHKey
sshKeyNames := strings.Split(os.Getenv("HETZNER_SSH_KEY_NAMES"), ",")
sshkeys, err := getSSHKeys(sshKeyNames...)
if err != nil {
return nil
}
serverOpts := hcloud.ServerCreateOpts{
Name: server,
StartAfterCreate: hcloud.Ptr(true),
SSHKeys: sshkeys,
Image: &hcloud.Image{Name: "docker-ce"},
ServerType: &hcloud.ServerType{Name: "cax11"},
Location: &hcloud.Location{Name: "fsn1"},
UserData: userdata,
PublicNet: &hcloud.ServerCreatePublicNet{EnableIPv4: true},
Labels: labels,
}
_, _, err = client.Server.Create(context.Background(), serverOpts)
if err != nil {
return err
}
}
return nil
}