* added HETZNER_SERVER_IMAGE
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/tag/woodpecker Pipeline was successful

* added HETZNER_SERVER_LOCATION
* added HETZNER_SERVER_MAX
* added HETZNER_SERVER_MIN
* added HETZNER_SERVER_TYPE
* exposing SCALER_POLL_INTERVAL in Dockerfile
* now logging error instead of quitting program
* added support for newer woodpecker server version 1.0.x
This commit is contained in:
Nils Stinnesbeck 2023-08-12 21:34:02 +02:00
parent bfda22db9c
commit 6f11eaa891
Signed by: nils
GPG Key ID: C52E07422A136076
5 changed files with 80 additions and 31 deletions

View File

@ -9,9 +9,15 @@ COPY --from=builder /jynx/jynx /jynx
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
ENTRYPOINT ["/jynx"]
ENV HCLOUD_API_TOKEN=""
ENV WOODPECKER_HOSTNAME=""
ENV WOODPECKER_AGENT_SECRET=""
ENV HCLOUD_PREFIX=""
ENV WOODPECKER_PROMETHEUS_AUTH_TOKEN=""
ENV HETZNER_CLOUD_API_TOKEN=""
ENV HETZNER_CLOUD_PREFIX=""
ENV HETZNER_SERVER_IMAGE=""
ENV HETZNER_SERVER_LOCATION=""
ENV HETZNER_SERVER_MAX=""
ENV HETZNER_SERVER_MIN=""
ENV HETZNER_SERVER_TYPE=""
ENV LOGLEVEL=""
ENV SCALER_POLL_INTERVAL=""
ENV WOODPECKER_AGENT_SECRET=""
ENV WOODPECKER_HOSTNAME=""
ENV WOODPECKER_PROMETHEUS_AUTH_TOKEN=""

View File

@ -22,12 +22,7 @@ type VersionInfo struct {
}
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))
return hcloud.NewClient(hcloud.WithToken(os.Getenv("HETZNER_CLOUD_API_TOKEN")))
}
func GetNewSuffix(servers []*hcloud.Server) (int, error) {
@ -100,7 +95,7 @@ func CheckServerDeletions(servers []*hcloud.Server) error {
pollIntervalSeconds %= 3600
}
for _, server := range servers {
for i, server := range servers {
safeInterval := time.Duration(3600-pollIntervalSeconds) * time.Second
optimalDeleteTime := server.Created.Add(safeInterval).Local()
log.PrintDebug(server.Name, "optimal delete time:", optimalDeleteTime)
@ -108,6 +103,21 @@ func CheckServerDeletions(servers []*hcloud.Server) error {
continue
}
// check if HETZNER_SERVER_MIN was provided as an environment variable
if minServersString := os.Getenv("HETZNER_SERVER_MIN"); minServersString != "" {
minServers, err := strconv.Atoi(minServersString)
if err != nil {
return err
}
// if the amount of servers is less than the minimal amount required
// leave this server running
if i < minServers {
log.PrintDebug("not deleting server", server.Name, "because HETZNER_SERVER_MIN is", minServers)
continue
}
}
// optimal delete time has passed, delete the server
if err := DeleteServer(server); err != nil {
return err
@ -199,16 +209,31 @@ func CreateServers(servers []string) error {
return nil
}
// get location and server type from environment
serverImage := os.Getenv("HETZNER_SERVER_IMAGE")
serverLocation := os.Getenv("HETZNER_SERVER_LOCATION")
serverType := os.Getenv("HETZNER_SERVER_TYPE")
// set default values for servers if none were provided
switch "" {
case serverImage:
serverImage = "docker-ce"
case serverLocation:
serverLocation = "fsn1"
case serverType:
serverType = "cax11"
}
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},
Image: &hcloud.Image{Name: serverImage},
Labels: labels,
Location: &hcloud.Location{Name: serverLocation},
Name: server,
PublicNet: &hcloud.ServerCreatePublicNet{EnableIPv4: true},
SSHKeys: sshkeys,
ServerType: &hcloud.ServerType{Name: serverType},
StartAfterCreate: hcloud.Ptr(true),
UserData: userdata,
}
_, _, err = client.Server.Create(context.Background(), serverOpts)
if err != nil {

View File

@ -61,7 +61,7 @@ func (s Server) GetStats() (*Stats, error) {
lines := strings.Split(string(b), "\n")
r := regexp.MustCompile(`^((woodpecker_)(worker_count|pending_jobs|running_jobs)) (\d+)`)
r := regexp.MustCompile(`^((woodpecker_)(worker_count|pending_jobs|pending_steps|running_jobs|running_steps)) (\d+)`)
var stats Stats
@ -76,9 +76,9 @@ func (s Server) GetStats() (*Stats, error) {
// assign stats to struct
switch matches[3] {
case "pending_jobs":
case "pending_jobs", "pending_steps":
stats.PendingJobs = parseStringToInt(matches[4])
case "running_jobs":
case "running_jobs", "running_steps":
stats.RunningJobs = parseStringToInt(matches[4])
case "worker_count":
stats.IdlingWorkers = parseStringToInt(matches[4])

12
main.go
View File

@ -35,13 +35,13 @@ func main() {
// start scaler once
if err := scaler.Start(&w); err != nil {
panic(err)
log.PrintError(err)
}
// start scaler every tick
for range ticker {
if err := scaler.Start(&w); err != nil {
panic(err)
log.PrintError(err)
}
}
}
@ -50,18 +50,18 @@ func checkEnv() {
switch "" {
case os.Getenv("WOODPECKER_HOSTNAME"):
log.PrintError("WOODPECKER_HOSTNAME not set")
os.Exit(1)
case os.Getenv("WOODPECKER_AGENT_SECRET"):
log.PrintError("WOODPECKER_AGENT_SECRET not set")
os.Exit(1)
case os.Getenv("HCLOUD_PREFIX"):
log.PrintError("HCLOUD_PREFIX not set")
os.Exit(1)
case os.Getenv("WOODPECKER_PROMETHEUS_AUTH_TOKEN"):
log.PrintError("WOODPECKER_PROMETHEUS_AUTH_TOKEN not set")
os.Exit(1)
case os.Getenv("HETZNER_SSH_KEY_NAMES"):
log.PrintError("HETZNER_SSH_KEY_NAMES not set")
os.Exit(1)
case os.Getenv("HETZNER_CLOUD_API_TOKEN"):
log.PrintError("please specify HETZNER_CLOUD_API_TOKEN")
os.Exit(1)
}
}

View File

@ -3,6 +3,7 @@ package scaler
import (
"fmt"
"os"
"strconv"
"git.stinnesbeck.com/go/jynx/backend/hetzner"
"git.stinnesbeck.com/go/jynx/ci/woodpecker"
@ -16,7 +17,12 @@ func Start(w *woodpecker.Server) error {
}
// get the prefix for jynx managed servers
prefix := os.Getenv("HCLOUD_PREFIX")
prefix := os.Getenv("HETZNER_CLOUD_PREFIX")
// set default prefix if none was provided
if prefix == "" {
prefix = "jynx"
}
// get a list of all servers from hetzner (from jynx)
servers, err := hetzner.ListJynxServers(prefix)
@ -51,14 +57,26 @@ func Start(w *woodpecker.Server) error {
return nil
}
// there are servers to be created
if maxServersString := os.Getenv("HETZNER_SERVER_MAX"); maxServersString != "" {
maxServers, err := strconv.Atoi(maxServersString)
if err != nil {
return err
}
// check if the maximum amount of servers is reached if one is provided
if newServers+len(servers) > maxServers && maxServers != 0 {
newServers = maxServers - len(servers)
}
}
// if there are no servers to be created, exit here
if newServers < 1 {
return nil
}
// slice to hold servers to be created
var ScaleUpServers []string
// there are servers to be created
// get the suffix for the starting server
suffix, err := hetzner.GetNewSuffix(servers)
for i := 0; i < newServers; i++ {