All checks were successful
continuous-integration/drone/push Build is passing
329 lines
10 KiB
Go
329 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
"vctp/db"
|
|
"vctp/internal/secrets"
|
|
"vctp/internal/settings"
|
|
"vctp/internal/tasks"
|
|
utils "vctp/internal/utils"
|
|
"vctp/internal/vcenter"
|
|
"vctp/log"
|
|
"vctp/server"
|
|
"vctp/server/router"
|
|
|
|
"github.com/go-co-op/gocron/v2"
|
|
)
|
|
|
|
var (
|
|
bindDisableTls bool
|
|
sha1ver string // sha1 revision used to build the program
|
|
buildTime string // when the executable was built
|
|
cronFrequency time.Duration
|
|
cronInvFrequency time.Duration
|
|
cronSnapshotFrequency time.Duration
|
|
cronAggregateFrequency time.Duration
|
|
encryptionKey = []byte("5L1l3B5KvwOCzUHMAlCgsgUTRAYMfSpa")
|
|
)
|
|
|
|
func main() {
|
|
settingsPath := flag.String("settings", "/etc/dtms/vctp.yml", "Path to settings YAML")
|
|
flag.Parse()
|
|
|
|
bootstrapLogger := log.New(log.LevelInfo, log.OutputText)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
// Load settings from yaml
|
|
s := settings.New(bootstrapLogger, *settingsPath)
|
|
err := s.ReadYMLSettings()
|
|
if err != nil {
|
|
bootstrapLogger.Error("failed to open yaml settings file", "error", err, "filename", *settingsPath)
|
|
os.Exit(1)
|
|
}
|
|
|
|
logger := log.New(
|
|
log.ToLevel(strings.ToLower(strings.TrimSpace(s.Values.Settings.LogLevel))),
|
|
log.ToOutput(strings.ToLower(strings.TrimSpace(s.Values.Settings.LogOutput))),
|
|
)
|
|
s.Logger = logger
|
|
|
|
// Configure database
|
|
dbDriver := strings.TrimSpace(s.Values.Settings.DatabaseDriver)
|
|
if dbDriver == "" {
|
|
dbDriver = "sqlite"
|
|
}
|
|
normalizedDriver := strings.ToLower(strings.TrimSpace(dbDriver))
|
|
if normalizedDriver == "" || normalizedDriver == "sqlite3" {
|
|
normalizedDriver = "sqlite"
|
|
}
|
|
dbURL := strings.TrimSpace(s.Values.Settings.DatabaseURL)
|
|
if dbURL == "" && normalizedDriver == "sqlite" {
|
|
dbURL = utils.GetFilePath("db.sqlite3")
|
|
}
|
|
|
|
database, err := db.New(logger, db.Config{Driver: normalizedDriver, DSN: dbURL})
|
|
if err != nil {
|
|
logger.Error("Failed to create database", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
defer database.Close()
|
|
//defer database.DB().Close()
|
|
|
|
if err = db.Migrate(database, normalizedDriver); err != nil {
|
|
logger.Error("failed to migrate database", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Determine bind IP
|
|
bindIP := strings.TrimSpace(s.Values.Settings.BindIP)
|
|
if bindIP == "" {
|
|
bindIP = utils.GetOutboundIP().String()
|
|
}
|
|
// Determine bind port
|
|
bindPort := s.Values.Settings.BindPort
|
|
if bindPort == 0 {
|
|
bindPort = 9443
|
|
}
|
|
bindAddress := fmt.Sprint(bindIP, ":", bindPort)
|
|
//logger.Info("Will listen on address", "ip", bindIP, "port", bindPort)
|
|
|
|
// Determine bind disable TLS
|
|
bindDisableTls = s.Values.Settings.BindDisableTLS
|
|
|
|
// Get file names for TLS cert/key
|
|
tlsCertFilename := strings.TrimSpace(s.Values.Settings.TLSCertFilename)
|
|
if tlsCertFilename != "" {
|
|
tlsCertFilename = utils.GetFilePath(tlsCertFilename)
|
|
} else {
|
|
tlsCertFilename = "./cert.pem"
|
|
}
|
|
|
|
tlsKeyFilename := strings.TrimSpace(s.Values.Settings.TLSKeyFilename)
|
|
if tlsKeyFilename != "" {
|
|
tlsKeyFilename = utils.GetFilePath(tlsKeyFilename)
|
|
} else {
|
|
tlsKeyFilename = "./privkey.pem"
|
|
}
|
|
|
|
// Generate certificate if required
|
|
if !(utils.FileExists(tlsCertFilename) && utils.FileExists(tlsKeyFilename)) {
|
|
logger.Warn("Specified TLS certificate or private key do not exist", "certificate", tlsCertFilename, "tls-key", tlsKeyFilename)
|
|
utils.GenerateCerts(tlsCertFilename, tlsKeyFilename)
|
|
}
|
|
|
|
// Load vcenter credentials from serttings, decrypt if required
|
|
a := secrets.New(logger, encryptionKey)
|
|
vcEp := strings.TrimSpace(s.Values.Settings.VcenterPassword)
|
|
if len(vcEp) == 0 {
|
|
logger.Error("No vcenter password configured")
|
|
os.Exit(1)
|
|
}
|
|
vcPass, err := a.Decrypt(vcEp)
|
|
if err != nil {
|
|
logger.Error("failed to decrypt vcenter credentials. Assuming un-encrypted", "error", err)
|
|
vcPass = []byte(vcEp)
|
|
if cipherText, encErr := a.Encrypt([]byte(vcEp)); encErr != nil {
|
|
logger.Warn("failed to encrypt vcenter credentials", "error", encErr)
|
|
} else {
|
|
s.Values.Settings.VcenterPassword = cipherText
|
|
if err := s.WriteYMLSettings(); err != nil {
|
|
logger.Warn("failed to update settings with encrypted vcenter password", "error", err)
|
|
} else {
|
|
logger.Info("encrypted vcenter password stored in settings file")
|
|
}
|
|
}
|
|
//os.Exit(1)
|
|
}
|
|
|
|
creds := vcenter.VcenterLogin{
|
|
Username: strings.TrimSpace(s.Values.Settings.VcenterUsername),
|
|
Password: string(vcPass),
|
|
Insecure: s.Values.Settings.VcenterInsecure,
|
|
}
|
|
if creds.Username == "" {
|
|
logger.Error("No vcenter username configured")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Prepare the task scheduler
|
|
c, err := gocron.NewScheduler()
|
|
if err != nil {
|
|
logger.Error("failed to create scheduler", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Pass useful information to the cron jobs
|
|
ct := &tasks.CronTask{
|
|
Logger: logger,
|
|
Database: database,
|
|
Settings: s,
|
|
VcCreds: &creds,
|
|
FirstHourlySnapshotCheck: true,
|
|
}
|
|
|
|
/*
|
|
cronFrequency = durationFromSeconds(s.Values.Settings.VcenterEventPollingSeconds, 60)
|
|
logger.Debug("Setting VM event polling cronjob frequency to", "frequency", cronFrequency)
|
|
|
|
cronInvFrequency = durationFromSeconds(s.Values.Settings.VcenterInventoryPollingSeconds, 7200)
|
|
logger.Debug("Setting VM inventory polling cronjob frequency to", "frequency", cronInvFrequency)
|
|
*/
|
|
|
|
cronSnapshotFrequency = durationFromSeconds(s.Values.Settings.VcenterInventorySnapshotSeconds, 3600)
|
|
logger.Debug("Setting VM inventory snapshot cronjob frequency to", "frequency", cronSnapshotFrequency)
|
|
|
|
cronAggregateFrequency = durationFromSeconds(s.Values.Settings.VcenterInventoryAggregateSeconds, 86400)
|
|
logger.Debug("Setting VM inventory daily aggregation cronjob frequency to", "frequency", cronAggregateFrequency)
|
|
|
|
/*
|
|
// start background processing for events stored in events table
|
|
startsAt := time.Now().Add(time.Second * 10)
|
|
job, err := c.NewJob(
|
|
gocron.DurationJob(cronFrequency),
|
|
gocron.NewTask(func() {
|
|
ct.RunVmCheck(ctx, logger)
|
|
}), gocron.WithSingletonMode(gocron.LimitModeReschedule),
|
|
gocron.WithStartAt(gocron.WithStartDateTime(startsAt)),
|
|
)
|
|
if err != nil {
|
|
logger.Error("failed to start event processing cron job", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
logger.Debug("Created event processing cron job", "job", job.ID(), "starting_at", startsAt)
|
|
*/
|
|
|
|
// start background checks of vcenter inventory
|
|
/*
|
|
startsAt2 := time.Now().Add(cronInvFrequency)
|
|
job2, err := c.NewJob(
|
|
gocron.DurationJob(cronInvFrequency),
|
|
gocron.NewTask(func() {
|
|
ct.RunVcenterPoll(ctx, logger)
|
|
}), gocron.WithSingletonMode(gocron.LimitModeReschedule),
|
|
gocron.WithStartAt(gocron.WithStartDateTime(startsAt2)),
|
|
)
|
|
if err != nil {
|
|
logger.Error("failed to start vcenter inventory cron job", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
logger.Debug("Created vcenter inventory cron job", "job", job2.ID(), "starting_at", startsAt2)
|
|
*/
|
|
|
|
startsAt3 := time.Now().Add(cronSnapshotFrequency)
|
|
if cronSnapshotFrequency == time.Hour {
|
|
startsAt3 = time.Now().Truncate(time.Hour).Add(time.Hour)
|
|
}
|
|
job3, err := c.NewJob(
|
|
gocron.DurationJob(cronSnapshotFrequency),
|
|
gocron.NewTask(func() {
|
|
ct.RunVcenterSnapshotHourly(ctx, logger)
|
|
}), gocron.WithSingletonMode(gocron.LimitModeReschedule),
|
|
gocron.WithStartAt(gocron.WithStartDateTime(startsAt3)),
|
|
)
|
|
if err != nil {
|
|
logger.Error("failed to start vcenter inventory snapshot cron job", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
logger.Debug("Created vcenter inventory snapshot cron job", "job", job3.ID(), "starting_at", startsAt3)
|
|
|
|
startsAt4 := time.Now().Add(cronAggregateFrequency)
|
|
if cronAggregateFrequency == time.Hour*24 {
|
|
now := time.Now()
|
|
startsAt4 = time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())
|
|
}
|
|
job4, err := c.NewJob(
|
|
gocron.DurationJob(cronAggregateFrequency),
|
|
gocron.NewTask(func() {
|
|
ct.RunVcenterDailyAggregate(ctx, logger)
|
|
}), gocron.WithSingletonMode(gocron.LimitModeReschedule),
|
|
gocron.WithStartAt(gocron.WithStartDateTime(startsAt4)),
|
|
)
|
|
if err != nil {
|
|
logger.Error("failed to start vcenter inventory aggregation cron job", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
logger.Debug("Created vcenter inventory aggregation cron job", "job", job4.ID(), "starting_at", startsAt4)
|
|
|
|
monthlyCron := "0 0 1 * *"
|
|
logger.Debug("Setting monthly aggregation cron schedule", "cron", monthlyCron)
|
|
job5, err := c.NewJob(
|
|
gocron.CronJob(monthlyCron, false),
|
|
gocron.NewTask(func() {
|
|
ct.RunVcenterMonthlyAggregate(ctx, logger)
|
|
}), gocron.WithSingletonMode(gocron.LimitModeReschedule),
|
|
)
|
|
if err != nil {
|
|
logger.Error("failed to start vcenter monthly aggregation cron job", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
logger.Debug("Created vcenter monthly aggregation cron job", "job", job5.ID())
|
|
|
|
snapshotCleanupCron := strings.TrimSpace(s.Values.Settings.SnapshotCleanupCron)
|
|
if snapshotCleanupCron == "" {
|
|
snapshotCleanupCron = "30 2 * * *"
|
|
}
|
|
job6, err := c.NewJob(
|
|
gocron.CronJob(snapshotCleanupCron, false),
|
|
gocron.NewTask(func() {
|
|
ct.RunSnapshotCleanup(ctx, logger)
|
|
}), gocron.WithSingletonMode(gocron.LimitModeReschedule),
|
|
)
|
|
if err != nil {
|
|
logger.Error("failed to start snapshot cleanup cron job", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
logger.Debug("Created snapshot cleanup cron job", "job", job6.ID())
|
|
|
|
// Retry failed hourly snapshots
|
|
retrySeconds := s.Values.Settings.HourlySnapshotRetrySeconds
|
|
if retrySeconds <= 0 {
|
|
retrySeconds = 300
|
|
}
|
|
job7, err := c.NewJob(
|
|
gocron.DurationJob(time.Duration(retrySeconds)*time.Second),
|
|
gocron.NewTask(func() {
|
|
ct.RunHourlySnapshotRetry(ctx, logger)
|
|
}), gocron.WithSingletonMode(gocron.LimitModeReschedule),
|
|
)
|
|
if err != nil {
|
|
logger.Error("failed to start hourly snapshot retry cron job", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
logger.Debug("Created hourly snapshot retry cron job", "job", job7.ID(), "interval_seconds", retrySeconds)
|
|
|
|
// start cron scheduler
|
|
c.Start()
|
|
|
|
// Start server
|
|
r := router.New(logger, database, buildTime, sha1ver, runtime.Version(), &creds, a, s)
|
|
svr := server.New(
|
|
logger,
|
|
c,
|
|
cancel,
|
|
bindAddress,
|
|
server.WithRouter(r),
|
|
server.SetTls(bindDisableTls),
|
|
server.SetCertificate(tlsCertFilename),
|
|
server.SetPrivateKey(tlsKeyFilename),
|
|
)
|
|
//logger.Debug("Server configured", "object", svr)
|
|
|
|
svr.StartAndWait()
|
|
|
|
os.Exit(0)
|
|
}
|
|
|
|
func durationFromSeconds(value int, fallback int) time.Duration {
|
|
if value <= 0 {
|
|
return time.Second * time.Duration(fallback)
|
|
}
|
|
return time.Second * time.Duration(value)
|
|
}
|