test cron job
Some checks are pending
CI / Lint (push) Waiting to run
CI / Test (push) Waiting to run
CI / End-to-End (push) Waiting to run
CI / Publish Docker (push) Blocked by required conditions
continuous-integration/drone/push Build is passing

This commit is contained in:
2024-09-13 14:50:04 +10:00
parent 144d887bda
commit 5042c4bfef
12 changed files with 268 additions and 69 deletions

View File

@@ -30,6 +30,7 @@ A few different technologies are configured to help getting off the ground easie
- The script `upgrade_htmx.sh` is available to make upgrading easier - The script `upgrade_htmx.sh` is available to make upgrading easier
- [goose](https://github.com/pressly/goose) for DB migrations - [goose](https://github.com/pressly/goose) for DB migrations
TODO: investigate https://github.com/DATA-DOG/go-sqlmock for testing
Technologies we're no longer using: Technologies we're no longer using:
- [golang migrate](https://github.com/golang-migrate/migrate) for DB migrations - [golang migrate](https://github.com/golang-migrate/migrate) for DB migrations

View File

@@ -0,0 +1,17 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE "Events" RENAME COLUMN Datacenter to DatacenterName;
ALTER TABLE "Events" RENAME COLUMN ComputeResource to ComputeResourceName;
ALTER TABLE "Events" ADD COLUMN DatacenterId TEXT;
ALTER TABLE "Events" ADD COLUMN ComputeResourceId TEXT;
ALTER TABLE "Events" ADD COLUMN VmName TEXT;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE "Events" DROP COLUMN VmName;
ALTER TABLE "Updates" DROP COLUMN ComputeResourceId;
ALTER TABLE "Updates" DROP COLUMN DatacenterId;
ALTER TABLE "Events" RENAME COLUMN ComputeResourceName to ComputeResource;
ALTER TABLE "Events" RENAME COLUMN DatacenterName to Datacenter;
-- +goose StatementEnd

View File

@@ -16,10 +16,13 @@ type Events struct {
ChainId string ChainId string
VmId sql.NullString VmId sql.NullString
EventKey sql.NullString EventKey sql.NullString
Datacenter sql.NullString DatacenterName sql.NullString
ComputeResource sql.NullString ComputeResourceName sql.NullString
UserName sql.NullString UserName sql.NullString
Processed int64 Processed int64
DatacenterId sql.NullString
ComputeResourceId sql.NullString
VmName sql.NullString
} }
type Inventory struct { type Inventory struct {

View File

@@ -32,9 +32,9 @@ RETURNING *;
-- name: CreateEvent :one -- name: CreateEvent :one
INSERT INTO "Events" ( INSERT INTO "Events" (
"CloudId", "Source", "EventTime", "ChainId", "VmId", "EventKey", "Datacenter", "ComputeResource", "UserName" "CloudId", "Source", "EventTime", "ChainId", "VmId", "VmName", "EventKey", "DatacenterId", "DatacenterName", "ComputeResourceId", "ComputeResourceName", "UserName"
) VALUES( ) VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ? ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
) )
RETURNING *; RETURNING *;

View File

@@ -12,11 +12,11 @@ import (
const createEvent = `-- name: CreateEvent :one const createEvent = `-- name: CreateEvent :one
INSERT INTO "Events" ( INSERT INTO "Events" (
"CloudId", "Source", "EventTime", "ChainId", "VmId", "EventKey", "Datacenter", "ComputeResource", "UserName" "CloudId", "Source", "EventTime", "ChainId", "VmId", "VmName", "EventKey", "DatacenterId", "DatacenterName", "ComputeResourceId", "ComputeResourceName", "UserName"
) VALUES( ) VALUES(
?, ?, ?, ?, ?, ?, ?, ?, ? ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
) )
RETURNING Eid, CloudId, Source, EventTime, ChainId, VmId, EventKey, Datacenter, ComputeResource, UserName, Processed RETURNING Eid, CloudId, Source, EventTime, ChainId, VmId, EventKey, DatacenterName, ComputeResourceName, UserName, Processed, DatacenterId, ComputeResourceId, VmName
` `
type CreateEventParams struct { type CreateEventParams struct {
@@ -25,9 +25,12 @@ type CreateEventParams struct {
EventTime sql.NullInt64 EventTime sql.NullInt64
ChainId string ChainId string
VmId sql.NullString VmId sql.NullString
VmName sql.NullString
EventKey sql.NullString EventKey sql.NullString
Datacenter sql.NullString DatacenterId sql.NullString
ComputeResource sql.NullString DatacenterName sql.NullString
ComputeResourceId sql.NullString
ComputeResourceName sql.NullString
UserName sql.NullString UserName sql.NullString
} }
@@ -38,9 +41,12 @@ func (q *Queries) CreateEvent(ctx context.Context, arg CreateEventParams) (Event
arg.EventTime, arg.EventTime,
arg.ChainId, arg.ChainId,
arg.VmId, arg.VmId,
arg.VmName,
arg.EventKey, arg.EventKey,
arg.Datacenter, arg.DatacenterId,
arg.ComputeResource, arg.DatacenterName,
arg.ComputeResourceId,
arg.ComputeResourceName,
arg.UserName, arg.UserName,
) )
var i Events var i Events
@@ -52,10 +58,13 @@ func (q *Queries) CreateEvent(ctx context.Context, arg CreateEventParams) (Event
&i.ChainId, &i.ChainId,
&i.VmId, &i.VmId,
&i.EventKey, &i.EventKey,
&i.Datacenter, &i.DatacenterName,
&i.ComputeResource, &i.ComputeResourceName,
&i.UserName, &i.UserName,
&i.Processed, &i.Processed,
&i.DatacenterId,
&i.ComputeResourceId,
&i.VmName,
) )
return i, err return i, err
} }
@@ -281,7 +290,7 @@ func (q *Queries) GetInventoryVmId(ctx context.Context, vmid sql.NullString) (In
} }
const listEvents = `-- name: ListEvents :many const listEvents = `-- name: ListEvents :many
SELECT Eid, CloudId, Source, EventTime, ChainId, VmId, EventKey, Datacenter, ComputeResource, UserName, Processed FROM "Events" SELECT Eid, CloudId, Source, EventTime, ChainId, VmId, EventKey, DatacenterName, ComputeResourceName, UserName, Processed, DatacenterId, ComputeResourceId, VmName FROM "Events"
ORDER BY "EventTime" ORDER BY "EventTime"
` `
@@ -302,10 +311,13 @@ func (q *Queries) ListEvents(ctx context.Context) ([]Events, error) {
&i.ChainId, &i.ChainId,
&i.VmId, &i.VmId,
&i.EventKey, &i.EventKey,
&i.Datacenter, &i.DatacenterName,
&i.ComputeResource, &i.ComputeResourceName,
&i.UserName, &i.UserName,
&i.Processed, &i.Processed,
&i.DatacenterId,
&i.ComputeResourceId,
&i.VmName,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -367,7 +379,7 @@ func (q *Queries) ListInventory(ctx context.Context) ([]Inventory, error) {
} }
const listUnprocessedEvents = `-- name: ListUnprocessedEvents :many const listUnprocessedEvents = `-- name: ListUnprocessedEvents :many
SELECT Eid, CloudId, Source, EventTime, ChainId, VmId, EventKey, Datacenter, ComputeResource, UserName, Processed FROM "Events" SELECT Eid, CloudId, Source, EventTime, ChainId, VmId, EventKey, DatacenterName, ComputeResourceName, UserName, Processed, DatacenterId, ComputeResourceId, VmName FROM "Events"
WHERE "Processed" = 0 WHERE "Processed" = 0
ORDER BY "EventTime" ORDER BY "EventTime"
` `
@@ -389,10 +401,13 @@ func (q *Queries) ListUnprocessedEvents(ctx context.Context) ([]Events, error) {
&i.ChainId, &i.ChainId,
&i.VmId, &i.VmId,
&i.EventKey, &i.EventKey,
&i.Datacenter, &i.DatacenterName,
&i.ComputeResource, &i.ComputeResourceName,
&i.UserName, &i.UserName,
&i.Processed, &i.Processed,
&i.DatacenterId,
&i.ComputeResourceId,
&i.VmName,
); err != nil { ); err != nil {
return nil, err return nil, err
} }

4
go.mod
View File

@@ -4,6 +4,7 @@ go 1.23.1
require ( require (
github.com/a-h/templ v0.2.778 github.com/a-h/templ v0.2.778
github.com/go-co-op/gocron/v2 v2.11.0
github.com/jmoiron/sqlx v1.4.0 github.com/jmoiron/sqlx v1.4.0
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/pressly/goose/v3 v3.22.0 github.com/pressly/goose/v3 v3.22.0
@@ -15,12 +16,15 @@ require (
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect github.com/mfridman/interpolate v0.0.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect golang.org/x/sys v0.25.0 // indirect
golang.org/x/tools v0.25.0 // indirect golang.org/x/tools v0.25.0 // indirect

10
go.sum
View File

@@ -6,6 +6,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-co-op/gocron/v2 v2.11.0 h1:IOowNA6SzwdRFnD4/Ol3Kj6G2xKfsoiiGq2Jhhm9bvE=
github.com/go-co-op/gocron/v2 v2.11.0/go.mod h1:xY7bJxGazKam1cz04EebrlP4S9q4iWdiAylMGP3jY9w=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@@ -20,6 +22,8 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -36,14 +40,20 @@ github.com/pressly/goose/v3 v3.22.0 h1:wd/7kNiPTuNAztWun7iaB98DrhulbWPrzMAaw2DEZ
github.com/pressly/goose/v3 v3.22.0/go.mod h1:yJM3qwSj2pp7aAaCvso096sguezamNb2OBgxCnh/EYg= github.com/pressly/goose/v3 v3.22.0/go.mod h1:yJM3qwSj2pp7aAaCvso096sguezamNb2OBgxCnh/EYg=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vmware/govmomi v0.43.0 h1:7Kg3Bkdly+TrE67BYXzRq7ZrDnn7xqpKX95uEh2f9Go= github.com/vmware/govmomi v0.43.0 h1:7Kg3Bkdly+TrE67BYXzRq7ZrDnn7xqpKX95uEh2f9Go=
github.com/vmware/govmomi v0.43.0/go.mod h1:IOv5nTXCPqH9qVJAlRuAGffogaLsNs8aF+e7vLgsHJU= github.com/vmware/govmomi v0.43.0/go.mod h1:IOv5nTXCPqH9qVJAlRuAGffogaLsNs8aF+e7vLgsHJU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=

View File

@@ -1,3 +1,76 @@
package tasks package tasks
import (
"context"
"log/slog"
"time"
"vctp/db"
"vctp/internal/vcenter"
)
// Handler handles requests.
type CronTask struct {
Logger *slog.Logger
Database db.Database
}
// use gocron to check events in the Events table // use gocron to check events in the Events table
func (c *CronTask) RunVmCheck(ctx context.Context, logger *slog.Logger) error {
var (
//unixTimestamp int64
numVcpus int32
numRam int32
datacenter string
)
logger.Debug("Started Events processing", "time", time.Now())
// Query events table
events, err := c.Database.Queries().ListUnprocessedEvents(ctx)
if err != nil {
logger.Error("Unable to query for unprocessed events", "error", err)
return nil // TODO - what to do with this error?
}
for _, evt := range events {
logger.Debug("Checking event", "event", evt)
c.Logger.Debug("connecting to vcenter")
vc := vcenter.New(c.Logger)
vc.Login(evt.Source)
//vmObject, err := vc.FindVMByName(vm.CloudEvent.Data.VM.Name)
//vmObject, err := vc.FindVMByID(vm.CloudEvent.Data.VM.VM.Value)
vmObject, err := vc.FindVMByIDWithDatacenter(evt.VmId.String, evt.DatacenterId.String)
if err != nil {
c.Logger.Error("Can't locate vm in vCenter", "vmID", evt.VmId.String, "error", err)
} else if vmObject == nil {
c.Logger.Debug("didn't find VM", "vm_id", evt.VmId.String)
numRam = 0
numVcpus = 0
datacenter = evt.DatacenterName.String
} else {
c.Logger.Debug("found VM")
//prettyPrint(vmObject)
// calculate VM properties we want to store
if vmObject.Vm.Config != nil {
numRam = vmObject.Vm.Config.Hardware.MemoryMB
numVcpus = vmObject.Vm.Config.Hardware.NumCPU * vmObject.Vm.Config.Hardware.NumCoresPerSocket
} else {
c.Logger.Error("Empty VM config")
}
}
err = vc.Logout()
if err != nil {
c.Logger.Error("unable to logout of vcenter", "error", err)
}
c.Logger.Debug("Simulate adding to Inventory", "vm_name", evt.VmName.String, "vcpus", numVcpus, "ram", numRam, "dc", datacenter)
}
//fmt.Printf("processing at %s", time.Now())
return nil
}

View File

@@ -25,7 +25,7 @@ type Vcenter struct {
type VmProperties struct { type VmProperties struct {
Vm mo.VirtualMachine Vm mo.VirtualMachine
Datacenter string //Datacenter string
} }
// New creates a new Vcenter with the given logger // New creates a new Vcenter with the given logger
@@ -159,14 +159,14 @@ func (v *Vcenter) FindVMByID(vmID string) (*VmProperties, error) {
if err == nil { if err == nil {
return &VmProperties{ return &VmProperties{
Datacenter: dc.Name(), //Datacenter: dc.Name(),
Vm: vm, Vm: vm,
}, nil }, nil
} else if _, ok := err.(*find.NotFoundError); !ok { } else if _, ok := err.(*find.NotFoundError); !ok {
// If the error is not a NotFoundError, return it // If the error is not a NotFoundError, return it
//return nil, fmt.Errorf("failed to retrieve VM with ID %s in datacenter %s: %w", vmID, dc.Name(), err) //return nil, fmt.Errorf("failed to retrieve VM with ID %s in datacenter %s: %w", vmID, dc.Name(), err)
v.Logger.Debug("Couldn't find vm in datacenter", "vm_id", vmID, "datacenter_name", dc.Name()) v.Logger.Debug("Couldn't find vm in datacenter", "vm_id", vmID, "datacenter_name", dc.Name())
} else if err != nil { } else {
return nil, fmt.Errorf("failed to retrieve VM: %w", err) return nil, fmt.Errorf("failed to retrieve VM: %w", err)
} }
} }
@@ -175,7 +175,7 @@ func (v *Vcenter) FindVMByID(vmID string) (*VmProperties, error) {
} }
func (v *Vcenter) FindVMByIDWithDatacenter(vmID string, dcID string) (*VmProperties, error) { func (v *Vcenter) FindVMByIDWithDatacenter(vmID string, dcID string) (*VmProperties, error) {
var dcName string //var dcName string
var err error var err error
v.Logger.Debug("searching for vm id", "vm_id", vmID, "datacenter_id", dcID) v.Logger.Debug("searching for vm id", "vm_id", vmID, "datacenter_id", dcID)
@@ -196,11 +196,13 @@ func (v *Vcenter) FindVMByIDWithDatacenter(vmID string, dcID string) (*VmPropert
// Use finder.SetDatacenter to set the datacenter // Use finder.SetDatacenter to set the datacenter
finder.SetDatacenter(datacenter) finder.SetDatacenter(datacenter)
/*
dcName, err = datacenter.ObjectName(v.ctx) dcName, err = datacenter.ObjectName(v.ctx)
if err != nil { if err != nil {
v.Logger.Error("Couldn't find the name of the datacenter", "error", err) v.Logger.Error("Couldn't find the name of the datacenter", "error", err)
dcName = "" dcName = ""
} }
*/
// Create a ManagedObjectReference for the VM // Create a ManagedObjectReference for the VM
vmRef := types.ManagedObjectReference{ vmRef := types.ManagedObjectReference{
@@ -214,14 +216,14 @@ func (v *Vcenter) FindVMByIDWithDatacenter(vmID string, dcID string) (*VmPropert
if err == nil { if err == nil {
v.Logger.Debug("Found VM", "vm", vm) v.Logger.Debug("Found VM", "vm", vm)
return &VmProperties{ return &VmProperties{
Datacenter: dcName, //Datacenter: dcName,
Vm: vm, Vm: vm,
}, nil }, nil
} else if _, ok := err.(*find.NotFoundError); !ok { } else if _, ok := err.(*find.NotFoundError); !ok {
// If the error is not a NotFoundError, return it // If the error is not a NotFoundError, return it
//return nil, fmt.Errorf("failed to retrieve VM with ID %s in datacenter %s: %w", vmID, dc.Name(), err) //return nil, fmt.Errorf("failed to retrieve VM with ID %s in datacenter %s: %w", vmID, dc.Name(), err)
v.Logger.Debug("Couldn't find vm in datacenter", "vm_id", vmID, "datacenter_name", dcName) v.Logger.Debug("Couldn't find vm in datacenter", "vm_id", vmID, "datacenter_id", dcID)
} else if err != nil { } else {
return nil, fmt.Errorf("failed to retrieve VM: %w", err) return nil, fmt.Errorf("failed to retrieve VM: %w", err)
} }

52
main.go
View File

@@ -1,15 +1,19 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"log/slog" "log/slog"
"os" "os"
"time"
"vctp/db" "vctp/db"
"vctp/internal/tasks"
utils "vctp/internal/utils" utils "vctp/internal/utils"
"vctp/log" "vctp/log"
"vctp/server" "vctp/server"
"vctp/server/router" "vctp/server/router"
"github.com/go-co-op/gocron/v2"
"github.com/joho/godotenv" "github.com/joho/godotenv"
) )
@@ -17,6 +21,7 @@ var (
bindDisableTls bool bindDisableTls bool
sha1ver string // sha1 revision used to build the program sha1ver string // sha1 revision used to build the program
buildTime string // when the executable was built buildTime string // when the executable was built
cronFrequency time.Duration
) )
func main() { func main() {
@@ -32,6 +37,8 @@ func main() {
log.GetOutput(), log.GetOutput(),
) )
ctx, cancel := context.WithCancel(context.Background())
// Configure database // Configure database
database, err := db.New(logger, "./db.sqlite3") database, err := db.New(logger, "./db.sqlite3")
if err != nil { if err != nil {
@@ -45,6 +52,19 @@ func main() {
return return
} }
// Prepare the task scheduler
s, err := gocron.NewScheduler()
if err != nil {
logger.Error("failed to create scheduler", "error", err)
os.Exit(1)
}
// Pass useful information to the cron jobs
c := &tasks.CronTask{
Logger: logger,
Database: database,
}
// Determine bind IP // Determine bind IP
bindIP := os.Getenv("BIND_IP") bindIP := os.Getenv("BIND_IP")
if bindIP == "" { if bindIP == "" {
@@ -85,9 +105,41 @@ func main() {
utils.GenerateCerts(tlsCertFilename, tlsKeyFilename) utils.GenerateCerts(tlsCertFilename, tlsKeyFilename)
} }
cronFrequencyString := os.Getenv("VCENTER_POLLING_SECONDS")
if cronFrequencyString != "" {
cronFrequency, err = time.ParseDuration(cronFrequencyString)
if err != nil {
slog.Error("Can't convert VCENTER_POLLING_SECONDS value to time duration. Defaulting to 60s", "value", cronFrequencyString, "error", err)
cronFrequency = time.Second * 60
}
} else {
cronFrequency = time.Second * 60
}
logger.Debug("Setting VM polling cronjob frequency to", "frequency", cronFrequency)
// start background processing
startsAt := time.Now().Add(time.Second * 10)
job, err := s.NewJob(
gocron.DurationJob(cronFrequency),
gocron.NewTask(func() {
c.RunVmCheck(ctx, logger)
}), gocron.WithSingletonMode(gocron.LimitModeReschedule),
gocron.WithStartAt(gocron.WithStartDateTime(startsAt)),
)
if err != nil {
logger.Error("failed to start cron jobs", "error", err)
os.Exit(1)
}
slog.Debug("Created cron job", "job", job)
s.Start()
// Start server // Start server
svr := server.New( svr := server.New(
logger, logger,
s,
cancel,
bindAddress, bindAddress,
server.WithRouter(router.New(logger, database)), server.WithRouter(router.New(logger, database)),
) )

View File

@@ -44,6 +44,8 @@ func (h *Handler) VmCreate(w http.ResponseWriter, r *http.Request) {
prettyPrint(event) prettyPrint(event)
} }
e := event.CloudEvent.Data
// Convert vmModel to CreateInventoryParams using the utility function // Convert vmModel to CreateInventoryParams using the utility function
//var params queries.CreateInventoryParams //var params queries.CreateInventoryParams
//db.ConvertToSQLParams(&vm, &params) //db.ConvertToSQLParams(&vm, &params)
@@ -65,15 +67,15 @@ func (h *Handler) VmCreate(w http.ResponseWriter, r *http.Request) {
vc.Login(event.CloudEvent.Source) vc.Login(event.CloudEvent.Source)
//vmObject, err := vc.FindVMByName(vm.CloudEvent.Data.VM.Name) //vmObject, err := vc.FindVMByName(vm.CloudEvent.Data.VM.Name)
//vmObject, err := vc.FindVMByID(vm.CloudEvent.Data.VM.VM.Value) //vmObject, err := vc.FindVMByID(vm.CloudEvent.Data.VM.VM.Value)
vmObject, err := vc.FindVMByIDWithDatacenter(event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Datacenter.Value) vmObject, err := vc.FindVMByIDWithDatacenter(e.VM.VM.Value, e.Datacenter.Datacenter.Value)
if err != nil { if err != nil {
h.Logger.Error("Can't locate vm in vCenter", "vmID", event.CloudEvent.Data.VM.VM.Value, "error", err) h.Logger.Error("Can't locate vm in vCenter", "vmID", e.VM.VM.Value, "error", err)
} else if vmObject == nil { } else if vmObject == nil {
h.Logger.Debug("didn't find VM", "vm_id", event.CloudEvent.Data.VM.VM.Value) h.Logger.Debug("didn't find VM", "vm_id", e.VM.VM.Value)
numRam = 0 numRam = 0
numVcpus = 0 numVcpus = 0
datacenter = event.CloudEvent.Data.Datacenter.Name datacenter = e.Datacenter.Name
} else { } else {
h.Logger.Debug("found VM") h.Logger.Debug("found VM")
//prettyPrint(vmObject) //prettyPrint(vmObject)
@@ -95,15 +97,16 @@ func (h *Handler) VmCreate(w http.ResponseWriter, r *http.Request) {
// Create an instance of CreateInventoryParams // Create an instance of CreateInventoryParams
h.Logger.Debug("Creating database parameters") h.Logger.Debug("Creating database parameters")
/* /*
params := queries.CreateInventoryParams{ params := queries.CreateInventoryParams{
Name: event.CloudEvent.Data.VM.Name, Name: e.VM.Name,
Vcenter: event.CloudEvent.Source, Vcenter: event.CloudEvent.Source,
EventId: sql.NullString{String: event.CloudEvent.ID, Valid: event.CloudEvent.ID != ""}, EventId: sql.NullString{String: event.CloudEvent.ID, Valid: event.CloudEvent.ID != ""},
EventKey: sql.NullString{String: strconv.Itoa(event.CloudEvent.Data.Key), Valid: strconv.Itoa(event.CloudEvent.Data.Key) != ""}, EventKey: sql.NullString{String: strconv.Itoa(e.Key), Valid: strconv.Itoa(e.Key) != ""},
VmId: sql.NullString{String: event.CloudEvent.Data.VM.VM.Value, Valid: event.CloudEvent.Data.VM.VM.Value != ""}, VmId: sql.NullString{String: e.VM.VM.Value, Valid: e.VM.VM.Value != ""},
Datacenter: sql.NullString{String: datacenter, Valid: datacenter != ""}, Datacenter: sql.NullString{String: datacenter, Valid: datacenter != ""},
Cluster: sql.NullString{String: event.CloudEvent.Data.ComputeResource.Name, Valid: event.CloudEvent.Data.ComputeResource.Name != ""}, Cluster: sql.NullString{String: e.ComputeResource.Name, Valid: e.ComputeResource.Name != ""},
CreationTime: sql.NullInt64{Int64: unixTimestamp, Valid: unixTimestamp > 0}, CreationTime: sql.NullInt64{Int64: unixTimestamp, Valid: unixTimestamp > 0},
InitialVcpus: sql.NullInt64{Int64: int64(numVcpus), Valid: numVcpus > 0}, InitialVcpus: sql.NullInt64{Int64: int64(numVcpus), Valid: numVcpus > 0},
InitialRam: sql.NullInt64{Int64: int64(numRam), Valid: numRam > 0}, InitialRam: sql.NullInt64{Int64: int64(numRam), Valid: numRam > 0},
@@ -114,12 +117,15 @@ func (h *Handler) VmCreate(w http.ResponseWriter, r *http.Request) {
Source: event.CloudEvent.Source, Source: event.CloudEvent.Source,
CloudId: event.CloudEvent.ID, CloudId: event.CloudEvent.ID,
EventTime: sql.NullInt64{Int64: unixTimestamp, Valid: unixTimestamp > 0}, EventTime: sql.NullInt64{Int64: unixTimestamp, Valid: unixTimestamp > 0},
ChainId: strconv.Itoa(event.CloudEvent.Data.ChainID), ChainId: strconv.Itoa(e.ChainID),
VmId: sql.NullString{String: event.CloudEvent.Data.VM.VM.Value, Valid: event.CloudEvent.Data.VM.VM.Value != ""}, VmId: sql.NullString{String: e.VM.VM.Value, Valid: e.VM.VM.Value != ""},
EventKey: sql.NullString{String: strconv.Itoa(event.CloudEvent.Data.Key), Valid: strconv.Itoa(event.CloudEvent.Data.Key) != ""}, VmName: sql.NullString{String: e.VM.Name, Valid: e.VM.Name != ""},
Datacenter: sql.NullString{String: event.CloudEvent.Data.Datacenter.Name, Valid: event.CloudEvent.Data.Datacenter.Name != ""}, EventKey: sql.NullString{String: strconv.Itoa(e.Key), Valid: strconv.Itoa(e.Key) != ""},
ComputeResource: sql.NullString{String: event.CloudEvent.Data.ComputeResource.Name, Valid: event.CloudEvent.Data.ComputeResource.Name != ""}, DatacenterName: sql.NullString{String: e.Datacenter.Name, Valid: e.Datacenter.Name != ""},
UserName: sql.NullString{String: event.CloudEvent.Data.UserName, Valid: event.CloudEvent.Data.UserName != ""}, DatacenterId: sql.NullString{String: e.Datacenter.Datacenter.Value, Valid: e.Datacenter.Datacenter.Value != ""},
ComputeResourceName: sql.NullString{String: e.ComputeResource.Name, Valid: e.ComputeResource.Name != ""},
ComputeResourceId: sql.NullString{String: e.ComputeResource.ComputeResource.Value, Valid: e.ComputeResource.ComputeResource.Value != ""},
UserName: sql.NullString{String: e.UserName, Valid: e.UserName != ""},
} }
h.Logger.Debug("database params", "params", params2) h.Logger.Debug("database params", "params", params2)
@@ -140,6 +146,7 @@ func (h *Handler) VmCreate(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Create Request : %v\n", result) fmt.Fprintf(w, "Create Request : %v\n", result)
} }
// prettyPrint comes from https://gist.github.com/sfate/9d45f6c5405dc4c9bf63bf95fe6d1a7c
func prettyPrint(args ...interface{}) { func prettyPrint(args ...interface{}) {
var caller string var caller string

View File

@@ -8,19 +8,23 @@ import (
"os" "os"
"os/signal" "os/signal"
"time" "time"
"github.com/go-co-op/gocron/v2"
) )
// Server represents an HTTP server. // Server represents an HTTP server.
type Server struct { type Server struct {
srv *http.Server srv *http.Server
logger *slog.Logger logger *slog.Logger
cron gocron.Scheduler
cancel context.CancelFunc
disableTls bool disableTls bool
tlsCertFilename string tlsCertFilename string
tlsKeyFilename string tlsKeyFilename string
} }
// New creates a new server with the given logger, address and options. // New creates a new server with the given logger, address and options.
func New(logger *slog.Logger, addr string, opts ...Option) *Server { func New(logger *slog.Logger, cron gocron.Scheduler, cancel context.CancelFunc, addr string, opts ...Option) *Server {
// Set some options for TLS // Set some options for TLS
tlsConfig := &tls.Config{ tlsConfig := &tls.Config{
@@ -50,6 +54,8 @@ func New(logger *slog.Logger, addr string, opts ...Option) *Server {
return &Server{ return &Server{
srv: srv, srv: srv,
logger: logger, logger: logger,
cron: cron,
cancel: cancel,
} }
} }
@@ -113,7 +119,6 @@ func (s *Server) Start() {
s.logger.Warn("failed to start server", "error", err) s.logger.Warn("failed to start server", "error", err)
} }
} }
}() }()
} }
@@ -133,6 +138,16 @@ func (s *Server) GracefulShutdown() {
// Doesn't block if no connections, but will otherwise wait // Doesn't block if no connections, but will otherwise wait
// until the timeout deadline. // until the timeout deadline.
_ = s.srv.Shutdown(ctx) _ = s.srv.Shutdown(ctx)
s.logger.Info("runing cron shutdown")
err := s.cron.Shutdown()
if err != nil {
s.logger.Error("error shutting cron", "error", err)
}
s.logger.Info("runing cancel")
s.cancel()
// Optionally, you could run srv.Shutdown in a goroutine and block on // Optionally, you could run srv.Shutdown in a goroutine and block on
// <-ctx.Done() if your application should wait for other services // <-ctx.Done() if your application should wait for other services
// to finalize based on context cancellation. // to finalize based on context cancellation.