* added HETZNER_SERVER_IMAGE
* 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:
parent
bfda22db9c
commit
6f11eaa891
16
Dockerfile
16
Dockerfile
@ -9,9 +9,15 @@ COPY --from=builder /jynx/jynx /jynx
|
|||||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
ENTRYPOINT ["/jynx"]
|
ENTRYPOINT ["/jynx"]
|
||||||
|
|
||||||
ENV HCLOUD_API_TOKEN=""
|
ENV HETZNER_CLOUD_API_TOKEN=""
|
||||||
ENV WOODPECKER_HOSTNAME=""
|
ENV HETZNER_CLOUD_PREFIX=""
|
||||||
ENV WOODPECKER_AGENT_SECRET=""
|
ENV HETZNER_SERVER_IMAGE=""
|
||||||
ENV HCLOUD_PREFIX=""
|
ENV HETZNER_SERVER_LOCATION=""
|
||||||
ENV WOODPECKER_PROMETHEUS_AUTH_TOKEN=""
|
ENV HETZNER_SERVER_MAX=""
|
||||||
|
ENV HETZNER_SERVER_MIN=""
|
||||||
|
ENV HETZNER_SERVER_TYPE=""
|
||||||
ENV LOGLEVEL=""
|
ENV LOGLEVEL=""
|
||||||
|
ENV SCALER_POLL_INTERVAL=""
|
||||||
|
ENV WOODPECKER_AGENT_SECRET=""
|
||||||
|
ENV WOODPECKER_HOSTNAME=""
|
||||||
|
ENV WOODPECKER_PROMETHEUS_AUTH_TOKEN=""
|
||||||
|
@ -22,12 +22,7 @@ type VersionInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func connect() *hcloud.Client {
|
func connect() *hcloud.Client {
|
||||||
token := os.Getenv("HCLOUD_API_TOKEN")
|
return hcloud.NewClient(hcloud.WithToken(os.Getenv("HETZNER_CLOUD_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) {
|
func GetNewSuffix(servers []*hcloud.Server) (int, error) {
|
||||||
@ -100,7 +95,7 @@ func CheckServerDeletions(servers []*hcloud.Server) error {
|
|||||||
pollIntervalSeconds %= 3600
|
pollIntervalSeconds %= 3600
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, server := range servers {
|
for i, server := range servers {
|
||||||
safeInterval := time.Duration(3600-pollIntervalSeconds) * time.Second
|
safeInterval := time.Duration(3600-pollIntervalSeconds) * time.Second
|
||||||
optimalDeleteTime := server.Created.Add(safeInterval).Local()
|
optimalDeleteTime := server.Created.Add(safeInterval).Local()
|
||||||
log.PrintDebug(server.Name, "optimal delete time:", optimalDeleteTime)
|
log.PrintDebug(server.Name, "optimal delete time:", optimalDeleteTime)
|
||||||
@ -108,6 +103,21 @@ func CheckServerDeletions(servers []*hcloud.Server) error {
|
|||||||
continue
|
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
|
// optimal delete time has passed, delete the server
|
||||||
if err := DeleteServer(server); err != nil {
|
if err := DeleteServer(server); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -199,16 +209,31 @@ func CreateServers(servers []string) error {
|
|||||||
return nil
|
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{
|
serverOpts := hcloud.ServerCreateOpts{
|
||||||
Name: server,
|
Image: &hcloud.Image{Name: serverImage},
|
||||||
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,
|
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)
|
_, _, err = client.Server.Create(context.Background(), serverOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -61,7 +61,7 @@ func (s Server) GetStats() (*Stats, error) {
|
|||||||
|
|
||||||
lines := strings.Split(string(b), "\n")
|
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
|
var stats Stats
|
||||||
|
|
||||||
@ -76,9 +76,9 @@ func (s Server) GetStats() (*Stats, error) {
|
|||||||
|
|
||||||
// assign stats to struct
|
// assign stats to struct
|
||||||
switch matches[3] {
|
switch matches[3] {
|
||||||
case "pending_jobs":
|
case "pending_jobs", "pending_steps":
|
||||||
stats.PendingJobs = parseStringToInt(matches[4])
|
stats.PendingJobs = parseStringToInt(matches[4])
|
||||||
case "running_jobs":
|
case "running_jobs", "running_steps":
|
||||||
stats.RunningJobs = parseStringToInt(matches[4])
|
stats.RunningJobs = parseStringToInt(matches[4])
|
||||||
case "worker_count":
|
case "worker_count":
|
||||||
stats.IdlingWorkers = parseStringToInt(matches[4])
|
stats.IdlingWorkers = parseStringToInt(matches[4])
|
||||||
|
12
main.go
12
main.go
@ -35,13 +35,13 @@ func main() {
|
|||||||
|
|
||||||
// start scaler once
|
// start scaler once
|
||||||
if err := scaler.Start(&w); err != nil {
|
if err := scaler.Start(&w); err != nil {
|
||||||
panic(err)
|
log.PrintError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// start scaler every tick
|
// start scaler every tick
|
||||||
for range ticker {
|
for range ticker {
|
||||||
if err := scaler.Start(&w); err != nil {
|
if err := scaler.Start(&w); err != nil {
|
||||||
panic(err)
|
log.PrintError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,18 +50,18 @@ func checkEnv() {
|
|||||||
switch "" {
|
switch "" {
|
||||||
case os.Getenv("WOODPECKER_HOSTNAME"):
|
case os.Getenv("WOODPECKER_HOSTNAME"):
|
||||||
log.PrintError("WOODPECKER_HOSTNAME not set")
|
log.PrintError("WOODPECKER_HOSTNAME not set")
|
||||||
|
os.Exit(1)
|
||||||
case os.Getenv("WOODPECKER_AGENT_SECRET"):
|
case os.Getenv("WOODPECKER_AGENT_SECRET"):
|
||||||
log.PrintError("WOODPECKER_AGENT_SECRET not set")
|
log.PrintError("WOODPECKER_AGENT_SECRET not set")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
case os.Getenv("HCLOUD_PREFIX"):
|
|
||||||
log.PrintError("HCLOUD_PREFIX not set")
|
|
||||||
os.Exit(1)
|
|
||||||
case os.Getenv("WOODPECKER_PROMETHEUS_AUTH_TOKEN"):
|
case os.Getenv("WOODPECKER_PROMETHEUS_AUTH_TOKEN"):
|
||||||
log.PrintError("WOODPECKER_PROMETHEUS_AUTH_TOKEN not set")
|
log.PrintError("WOODPECKER_PROMETHEUS_AUTH_TOKEN not set")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
case os.Getenv("HETZNER_SSH_KEY_NAMES"):
|
case os.Getenv("HETZNER_SSH_KEY_NAMES"):
|
||||||
log.PrintError("HETZNER_SSH_KEY_NAMES not set")
|
log.PrintError("HETZNER_SSH_KEY_NAMES not set")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
case os.Getenv("HETZNER_CLOUD_API_TOKEN"):
|
||||||
|
log.PrintError("please specify HETZNER_CLOUD_API_TOKEN")
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package scaler
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"git.stinnesbeck.com/go/jynx/backend/hetzner"
|
"git.stinnesbeck.com/go/jynx/backend/hetzner"
|
||||||
"git.stinnesbeck.com/go/jynx/ci/woodpecker"
|
"git.stinnesbeck.com/go/jynx/ci/woodpecker"
|
||||||
@ -16,7 +17,12 @@ func Start(w *woodpecker.Server) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get the prefix for jynx managed servers
|
// 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)
|
// get a list of all servers from hetzner (from jynx)
|
||||||
servers, err := hetzner.ListJynxServers(prefix)
|
servers, err := hetzner.ListJynxServers(prefix)
|
||||||
@ -51,14 +57,26 @@ func Start(w *woodpecker.Server) error {
|
|||||||
return nil
|
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 {
|
if newServers < 1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// slice to hold servers to be created
|
||||||
var ScaleUpServers []string
|
var ScaleUpServers []string
|
||||||
|
|
||||||
|
// there are servers to be created
|
||||||
// get the suffix for the starting server
|
// get the suffix for the starting server
|
||||||
suffix, err := hetzner.GetNewSuffix(servers)
|
suffix, err := hetzner.GetNewSuffix(servers)
|
||||||
for i := 0; i < newServers; i++ {
|
for i := 0; i < newServers; i++ {
|
||||||
|
Loading…
Reference in New Issue
Block a user