jynx/backend/hetzner/servers.go

186 lines
4.8 KiB
Go
Raw Normal View History

2023-05-29 21:21:50 +00:00
package hetzner
import (
"context"
"fmt"
"os"
"regexp"
2023-07-15 19:51:31 +00:00
"strconv"
2023-05-29 21:21:50 +00:00
"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))
}
2023-07-15 19:51:31 +00:00
func GetNewSuffix(servers []*hcloud.Server) (int, error) {
// cop out if there are no servers yet
if len(servers) == 0 {
return 1, nil
2023-05-29 21:21:50 +00:00
}
2023-07-15 19:51:31 +00:00
// variable to hold highest number
var highestNumber int
2023-05-29 21:21:50 +00:00
2023-07-15 19:51:31 +00:00
// 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
2023-05-29 21:21:50 +00:00
}
2023-07-15 19:51:31 +00:00
// ListJynxServers lists all servers managed by Jynx hosted on Hetzner Cloud
func ListJynxServers(prefix string) ([]*hcloud.Server, error) {
// connect to the hcloud API
2023-05-29 21:21:50 +00:00
client := connect()
2023-07-15 19:51:31 +00:00
// get all servers
2023-05-29 21:21:50 +00:00
servers, err := client.Server.All(context.Background())
if err != nil {
return nil, err
}
2023-07-15 19:51:31 +00:00
// create object that holds all servers managed by Jynx
var jynxServers []*hcloud.Server
2023-05-29 21:21:50 +00:00
2023-07-15 19:51:31 +00:00
// 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) {
2023-05-29 21:21:50 +00:00
continue
}
2023-07-15 19:51:31 +00:00
// optimal delete time has passed, delete the server
if err := DeleteServer(server); err != nil {
return err
}
2023-05-29 21:21:50 +00:00
}
2023-07-15 19:51:31 +00:00
return nil
2023-05-29 21:21:50 +00:00
}
2023-07-15 19:51:31 +00:00
func DeleteServer(server *hcloud.Server) error {
client := connect()
log.PrintInfo("Deleting", server.Name)
_, response, err := client.Server.DeleteWithResult(context.Background(), server)
2023-07-15 19:51:31 +00:00
if err != nil {
return err
}
if response.StatusCode != 200 {
return fmt.Errorf("api returned errorcode %d", response.StatusCode)
}
2023-07-15 19:51:31 +00:00
return nil
}
func CreateServers(servers []string) error {
// connect to hcloud API
2023-05-29 21:21:50 +00:00
client := connect()
2023-07-15 19:51:31 +00:00
// read in some environment variables
2023-05-29 21:21:50 +00:00
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
2023-05-29 21:21:50 +00:00
2023-07-15 19:51:31 +00:00
// 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)
2023-05-29 21:21:50 +00:00
2023-07-15 19:51:31 +00:00
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())
2023-05-29 21:21:50 +00:00
2023-07-15 19:51:31 +00:00
var sshkeys []*hcloud.SSHKey
sshKeyNames := strings.Split(os.Getenv("HETZNER_SSH_KEY_NAMES"), ",")
2023-05-29 21:21:50 +00:00
sshkeys, err := getSSHKeys(sshKeyNames...)
2023-07-15 19:51:31 +00:00
if err != nil {
return nil
}
2023-05-29 21:21:50 +00:00
2023-07-15 19:51:31 +00:00
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
}
2023-05-29 21:21:50 +00:00
}
return nil
}