Skip to content

Commit

Permalink
added new global variables and ENVs for database + cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
NorseGaud committed Oct 21, 2024
1 parent f036196 commit 79618bd
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 82 deletions.
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,9 @@ build-linux:
GOOS=linux OS_TYPE=linux $(MAKE) go.build

run-docker-compose:
cp dist/anklet_v$(VERSION)*_linux_$(ARCH).zip docker/
cp dist/anklet_v$(VERSION)*_linux_$(ARCH) docker/
cd docker && \
rm -f anklet_linux_$(ARCH) && \
unzip anklet_v$(VERSION)*_linux_$(ARCH).zip && \
mv anklet_v$(VERSION)*_linux_$(ARCH) anklet_linux_$(ARCH) && \
rm -f anklet_v$(VERSION)*_linux_$(ARCH).zip && \
docker-compose up --build --force-recreate
52 changes: 22 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ With the github plugin, there is a Receiver Plugin and a Handler Plugin.
work_dir: /tmp/
pid_file_dir: /tmp/
# plugins_path: ~/.config/anklet/plugins/
global_database_url: localhost
global_database_port: 6379
global_database_user: ""
global_database_password: ""
global_database_database: 0
global_private_key: /Users/nathanpierce/veertuinc-anklet.2024-07-19.private-key.pem
log:
# if file_dir is not set, it will be set to current directory you execute anklet in
file_dir: /Users/myUser/Library/Logs/
Expand All @@ -86,48 +92,32 @@ With the github plugin, there is a Receiver Plugin and a Handler Plugin.
hook_id: 489747753
port: 54321 # port that's open to the internet so github can post to it
secret: 00000000
private_key: /Users/nathanpierce/veertuinc-anklet.2024-07-19.private-key.pem
#private_key: /Users/nathanpierce/veertuinc-anklet.2024-07-19.private-key.pem
app_id: 949431
installation_id: 52970581
repo: anklet
owner: veertuinc
database:
enabled: true
url: localhost
port: 6379
user: ""
password: ""
database: 0
#database:
#url: localhost
#port: 6379
#user: ""
#password: ""
#database: 0
# GITHUB HANDLERS
- name: RUNNER1
plugin: github
private_key: /Users/nathanpierce/veertuinc-anklet.2024-07-19.private-key.pem
app_id: 949431
installation_id: 52970581
repo: anklet
owner: veertuinc
registry_url: http://anka.registry:8089
sleep_interval: 10 # sleep 10 seconds between checks for new jobs
database:
enabled: true
url: localhost
port: 6379
user: ""
password: ""
database: 0
- name: RUNNER2
plugin: github
token: github_pat_1XXXXX
repo: anklet
owner: veertuinc
registry_url: http://anka.registry:8089
database:
enabled: true
url: localhost
port: 6379
user: ""
password: ""
database: 0

```
> Note: You can only ever run two VMs per host per the Apple macOS SLA. While you can specify more than two plugins, only two will ever be running a VM at one time. `sleep_interval` can be used to control the frequency/priority of a plugin and increase the odds that a job will be picked up.
Expand Down Expand Up @@ -158,6 +148,10 @@ tail -fF /opt/homebrew/var/log/redis.log

For production, we recommend running a [redis cluster](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/) on infrastructure that is separate from your Anklet hosts and has guaranteed uptime.

Your config.yml file must define the database in one of the following ways:
- Using the `database` variables (under each plugin).
- Using the `global_database_*` variables (applies to and overrides the `database` variables under each plugin).

### Plugin Setup and Usage Guides

You can control the location plugins are stored on the host by setting the `plugins_path` in the `config.yml` file. If not set, it will default to `~/.config/anklet/plugins/`.
Expand Down Expand Up @@ -298,6 +292,11 @@ pid_file_dir: /tmp/
log:
# if file_dir is not set, it will be set to current directory you execute anklet in
file_dir: /Users/nathanpierce/Library/Logs/
global_database_url: localhost
global_database_port: 6379
global_database_user: ""
global_database_password: ""
global_database_database: 0
metrics:
aggregator: true
metrics_urls:
Expand All @@ -306,13 +305,6 @@ metrics:
- http://192.168.1.203:8080/metrics
port: 8081 # port to serve aggregator on
sleep_interval: 10 # how often to fetch metrics from each Anklet defined
database:
enabled: true
url: localhost
port: 6379
user: ""
password: ""
database: 0
```
You can see that this requires a database to be running. The aggregator will store the metrics in Redis so that it can serve them up without delay.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.7.0
0.8.0
13 changes: 10 additions & 3 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ services:
container_name: anklet
volumes:
- /Users/nathanpierce/.config/anklet:/root/.config/anklet/
- /Users/nathanpierce/veertuinc-anklet.2024-10-09.private-key.pem:/private-key.pem
ports:
- "8081:8081"
environment:
# - LOG_LEVEL=debug
# - LOG_LEVEL=dev
- ANKLET_CONFIG_FILE_NAME=org-receiver-config.yml
##################################################################################
# ANKLET_GLOBAL_PRIVATE_KEY allows you to use the same private key for all runners
# instead of defining a different one in the config.yml file for each runner.
# You can also set it to be the PEM contents, vs pointing to a file.
# - ANKLET_GLOBAL_PRIVATE_KEY=/Users/nathanpierce/veertuinc-anklet.2024-07-19.private-key.pem
- ANKLET_GLOBAL_PRIVATE_KEY=/private-key.pem
# - |-
# ANKLET_GLOBAL_PRIVATE_KEY=-----BEGIN RSA PRIVATE KEY-----
# MIIEpAIBAAKCAQEAyqZDXOEzV5gRocWAhH73chGjn4HBh1UCG2Du4v9JmcjfyYT3
Expand All @@ -35,4 +36,10 @@ services:
# - ANKLET_METRICS_DATABASE_USER=
# - ANKLET_METRICS_DATABASE_PASSWORD=
# - ANKLET_METRICS_DATABASE_DATABASE=0

##################################################################################
# GLOBAL DATABASE ENVs: if set, they will be used instead of the definitions in the config .yml file
- ANKLET_GLOBAL_DATABASE_URL=host.docker.internal
- ANKLET_GLOBAL_DATABASE_PORT=6379
- ANKLET_GLOBAL_DATABASE_USER=
- ANKLET_GLOBAL_DATABASE_PASSWORD=
- ANKLET_GLOBAL_DATABASE_DATABASE=0
48 changes: 35 additions & 13 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@ import (
type ContextKey string

type Config struct {
Plugins []Plugin `yaml:"plugins"`
Log Log `yaml:"log"`
PidFileDir string `yaml:"pid_file_dir"`
LogFileDir string `yaml:"log_file_dir"`
WorkDir string `yaml:"work_dir"`
Metrics Metrics `yaml:"metrics"`
GlobalPrivateKey string `yaml:"global_private_key"`
PluginsPath string `yaml:"plugins_path"`
Plugins []Plugin `yaml:"plugins"`
Log Log `yaml:"log"`
PidFileDir string `yaml:"pid_file_dir"`
LogFileDir string `yaml:"log_file_dir"`
WorkDir string `yaml:"work_dir"`
Metrics Metrics `yaml:"metrics"`
GlobalPrivateKey string `yaml:"global_private_key"`
PluginsPath string `yaml:"plugins_path"`
GlobalDatabaseURL string `yaml:"global_database_url"`
GlobalDatabasePort int `yaml:"global_database_port"`
GlobalDatabaseUser string `yaml:"global_database_user"`
GlobalDatabasePassword string `yaml:"global_database_password"`
GlobalDatabaseDatabase int `yaml:"global_database_database"`
}

type Log struct {
Expand All @@ -43,7 +48,6 @@ type Database struct {
User string `yaml:"user"`
Password string `yaml:"password"`
Database int `yaml:"database"`
Enabled bool `yaml:"enabled"`
}

type Workflow struct {
Expand Down Expand Up @@ -110,10 +114,6 @@ func LoadInEnvs(config Config) (Config, error) {
}
config.Metrics.SleepInterval = value
}
envDBEnabled := os.Getenv("ANKLET_METRICS_DATABASE_ENABLED")
if envDBEnabled != "" {
config.Metrics.Database.Enabled = envDBEnabled == "true"
}
envDBUser := os.Getenv("ANKLET_METRICS_DATABASE_USER")
if envDBUser != "" {
config.Metrics.Database.User = envDBUser
Expand Down Expand Up @@ -152,6 +152,28 @@ func LoadInEnvs(config Config) (Config, error) {
if envGlobalPrivateKey != "" {
config.GlobalPrivateKey = envGlobalPrivateKey
}

envGlobalDatabaseURL := os.Getenv("ANKLET_GLOBAL_DATABASE_URL")
if envGlobalDatabaseURL != "" {
config.GlobalDatabaseURL = envGlobalDatabaseURL
}
envGlobalDatabasePort := os.Getenv("ANKLET_GLOBAL_DATABASE_PORT")
if envGlobalDatabasePort != "" {
port, err := strconv.Atoi(envGlobalDatabasePort)
if err != nil {
return Config{}, err
}
config.GlobalDatabasePort = port
}
envGlobalDatabaseUser := os.Getenv("ANKLET_GLOBAL_DATABASE_USER")
if envGlobalDatabaseUser != "" {
config.GlobalDatabaseUser = envGlobalDatabaseUser
}
envGlobalDatabasePassword := os.Getenv("ANKLET_GLOBAL_DATABASE_PASSWORD")
if envGlobalDatabasePassword != "" {
config.GlobalDatabasePassword = envGlobalDatabasePassword
}

// pidFileDir := os.Getenv("ANKLET_PID_FILE_DIR")
// if pidFileDir != "" {
// config.PidFileDir = pidFileDir
Expand Down
5 changes: 2 additions & 3 deletions internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ func NewClient(ctx context.Context, config config.Database) (*Database, error) {

logging.DevDebug(ctx, "pinging redis client")
ping := rdb.Ping(ctx)
if ping.Err() != nil {
logging.DevDebug(ctx, fmt.Sprintf("error pinging redis client: %v", ping.Err()))
return nil, ping.Err()
if ping.Err() != nil && ping.Err().Error() != "" {
return nil, errors.New("error pinging redis client: " + ping.Err().Error())
}
pong, err := ping.Result()
if err != nil {
Expand Down
55 changes: 48 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,21 @@ func worker(parentCtx context.Context, logger *slog.Logger, loadedConfig config.
}
}
}()

// set global database variables
var databaseURL string
var databasePort int
var databaseUser string
var databasePassword string
var databaseDatabase int
if loadedConfig.GlobalDatabaseURL != "" {
databaseURL = loadedConfig.GlobalDatabaseURL
databasePort = loadedConfig.GlobalDatabasePort
databaseUser = loadedConfig.GlobalDatabaseUser
databasePassword = loadedConfig.GlobalDatabasePassword
databaseDatabase = loadedConfig.GlobalDatabaseDatabase
}

// Setup Metrics Server and context
metricsPort := "8080"
if loadedConfig.Metrics.Port != "" {
Expand All @@ -246,7 +261,20 @@ func worker(parentCtx context.Context, logger *slog.Logger, loadedConfig config.
metricsService := metrics.NewServer(metricsPort)
if loadedConfig.Metrics.Aggregator {
workerCtx = logging.AppendCtx(workerCtx, slog.Any("metrics_urls", loadedConfig.Metrics.MetricsURLs))
databaseContainer, err := database.NewClient(workerCtx, loadedConfig.Metrics.Database)
if databaseURL == "" { // if no global database URL is set, use the metrics database URL
databaseURL = loadedConfig.Metrics.Database.URL
databasePort = loadedConfig.Metrics.Database.Port
databaseUser = loadedConfig.Metrics.Database.User
databasePassword = loadedConfig.Metrics.Database.Password
databaseDatabase = loadedConfig.Metrics.Database.Database
}
databaseContainer, err := database.NewClient(workerCtx, config.Database{
URL: databaseURL,
Port: databasePort,
User: databaseUser,
Password: databasePassword,
Database: databaseDatabase,
})
if err != nil {
panic(fmt.Sprintf("unable to access database: %v", err))
}
Expand Down Expand Up @@ -326,12 +354,11 @@ func worker(parentCtx context.Context, logger *slog.Logger, loadedConfig config.

if plugin.Repo == "" {
logger.InfoContext(pluginCtx, "no repo set for plugin; assuming it's an organization level plugin")
logging.DevDebug(pluginCtx, "setting isRepoSet to false")
pluginCtx = context.WithValue(pluginCtx, config.ContextKey("isRepoSet"), false)
logging.DevDebug(pluginCtx, "set isRepoSet to false")
} else {
pluginCtx = context.WithValue(pluginCtx, config.ContextKey("isRepoSet"), true)
logging.DevDebug(pluginCtx, "setting isRepoSet to true")
logging.DevDebug(pluginCtx, "set isRepoSet to true")
}

if plugin.PrivateKey == "" && loadedConfig.GlobalPrivateKey != "" {
Expand All @@ -354,12 +381,26 @@ func worker(parentCtx context.Context, logger *slog.Logger, loadedConfig config.
logging.DevDebug(pluginCtx, "loaded the anka CLI")
}

if plugin.Database.Enabled {
if databaseURL != "" || plugin.Database.URL != "" {
if databaseURL == "" {
databaseURL = plugin.Database.URL
databasePort = plugin.Database.Port
databaseUser = plugin.Database.User
databasePassword = plugin.Database.Password
databaseDatabase = plugin.Database.Database
}
logging.DevDebug(pluginCtx, "connecting to database")
databaseClient, err := database.NewClient(pluginCtx, plugin.Database)
databaseClient, err := database.NewClient(pluginCtx, config.Database{
URL: databaseURL,
Port: databasePort,
User: databaseUser,
Password: databasePassword,
Database: databaseDatabase,
})
if err != nil {
panic(fmt.Sprintf("unable to access database: %v", err))
panic("unable to access database: " + err.Error())
}
logger.InfoContext(pluginCtx, "connected to database", slog.Any("database", databaseClient))
pluginCtx = context.WithValue(pluginCtx, config.ContextKey("database"), databaseClient)
logging.DevDebug(pluginCtx, "connected to database")
}
Expand All @@ -378,7 +419,6 @@ func worker(parentCtx context.Context, logger *slog.Logger, loadedConfig config.
logging.DevDebug(pluginCtx, "plugin for loop::default")
run.Plugin(workerCtx, pluginCtx, pluginCancel, logger, firstPluginStarted, metricsData)
if workerCtx.Err() != nil || toRunOnce == "true" {
logging.DevDebug(pluginCtx, "plugin for loop::default::workerCtx.Err() != nil || toRunOnce == \"true\"")
pluginCancel()
logger.WarnContext(pluginCtx, shutDownMessage)
return
Expand All @@ -403,6 +443,7 @@ func worker(parentCtx context.Context, logger *slog.Logger, loadedConfig config.
go metricsService.Start(workerCtx, logger, soloReceiver)
}
wg.Wait()
time.Sleep(time.Second) // prevents exiting before the logger has a chance to write the final log entry (from panics)
logger.WarnContext(workerCtx, "anklet (and all plugins) shut down")
os.Exit(0)
}
16 changes: 8 additions & 8 deletions plugins/handlers/github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ plugins:
registry_url: http://anka.registry:8089
runner_group: macOS # requires Enterprise github
# sleep_interval: 5 # Optional; defaults to 1 second.
database:
enabled: true
url: localhost
port: 6379
user: ""
password: ""
database: 0
#database:
# enabled: true
# url: localhost
# port: 6379
# user: ""
# password: ""
# database: 0
```

- Your PAT or Github App must have **Actions** and **Administration** Read & Write permissions.
- The `database` is required. You can find installation instructions in the anklet main [README.md](../../README.md#database-setup).
- You must define the database in the config.yml file either using the `database` section or the `global_database_*` variables. You can find installation instructions in the anklet main [README.md](../../README.md#database-setup).
- If you are attempting to register runners for an entire organization, do NOT set `repo` and make sure your Github App has `Self-hosted runners` > `Read and write` permissions.
- If your Organization level runner is registered and your public repo jobs are not picking it up even though the labels are a perfect match, make sure the Runner groups (likely `Default`) has `Allow public repositories`.

Expand Down
13 changes: 6 additions & 7 deletions plugins/receivers/github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ plugins:
repo: anklet
owner: veertuinc
skip_redeliver: true
database:
enabled: true
url: localhost
port: 6379
user: ""
password: ""
database: 0
#database:
# url: localhost
# port: 6379
# user: ""
# password: ""
# database: 0
```

- If you leave off `repo`, the receiver will be an organization level receiver.
Expand Down
Loading

0 comments on commit 79618bd

Please sign in to comment.