database insert is working
Some checks failed
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 failing

This commit is contained in:
2024-09-12 15:03:24 +10:00
parent 0d2f983eb3
commit 6b285e55b8
9 changed files with 165 additions and 28 deletions

View File

@@ -1,8 +1,10 @@
package db package db
import ( import (
"database/sql"
"embed" "embed"
"log/slog" "log/slog"
"reflect"
"vctp/db/queries" "vctp/db/queries"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
@@ -66,3 +68,33 @@ func Migrate(db Database) error {
return nil return nil
} }
// ConvertToSQLParams is a utility function that generically converts a struct to a corresponding sqlc-generated struct
func ConvertToSQLParams(input interface{}, output interface{}) {
inputVal := reflect.ValueOf(input).Elem()
outputVal := reflect.ValueOf(output).Elem()
for i := 0; i < outputVal.NumField(); i++ {
outputField := outputVal.Field(i)
inputField := inputVal.FieldByName(outputVal.Type().Field(i).Name)
if !inputField.IsValid() || !outputField.CanSet() {
continue
}
switch outputField.Type() {
case reflect.TypeOf(sql.NullString{}):
if inputField.Kind() == reflect.Ptr && inputField.IsNil() {
outputField.Set(reflect.ValueOf(sql.NullString{Valid: false}))
} else {
outputField.Set(reflect.ValueOf(sql.NullString{String: inputField.String(), Valid: true}))
}
case reflect.TypeOf(sql.NullInt64{}):
if inputField.Int() == 0 {
outputField.Set(reflect.ValueOf(sql.NullInt64{Valid: false}))
} else {
outputField.Set(reflect.ValueOf(sql.NullInt64{Int64: inputField.Int(), Valid: true}))
}
}
}
}

View File

@@ -2,13 +2,13 @@
-- +goose StatementBegin -- +goose StatementBegin
CREATE TABLE IF NOT EXISTS "Inventory" ( CREATE TABLE IF NOT EXISTS "Inventory" (
"Iid" INTEGER UNIQUE, "Iid" INTEGER UNIQUE,
"Name" TEXT, "Name" TEXT NOT NULL,
"Vcenter" TEXT, "Vcenter" TEXT NOT NULL,
"VmId" TEXT, "VmId" TEXT,
"EventKey" TEXT, "EventKey" TEXT,
"EventId" TEXT, "EventId" TEXT,
"CreationTime" TEXT, "CreationTime" INTEGER,
"DeletionTime" TEXT, "DeletionTime" INTEGER,
"ResourcePool" TEXT, "ResourcePool" TEXT,
"VmType" TEXT, "VmType" TEXT,
"Datacenter" TEXT, "Datacenter" TEXT,
@@ -23,8 +23,8 @@ CREATE TABLE IF NOT EXISTS "Inventory" (
CREATE TABLE IF NOT EXISTS "Updates" ( CREATE TABLE IF NOT EXISTS "Updates" (
"Uid" INTEGER UNIQUE, "Uid" INTEGER UNIQUE,
"InventoryId" INTEGER, "InventoryId" INTEGER,
"UpdateTime" TEXT, "UpdateTime" INTEGER,
"UpdateType" TEXT, "UpdateType" TEXT NOT NULL,
"NewVcpus" INTEGER, "NewVcpus" INTEGER,
"NewRam" INTEGER, "NewRam" INTEGER,
"NewResourcePool" TEXT "NewResourcePool" TEXT

View File

@@ -10,13 +10,13 @@ import (
type Inventory struct { type Inventory struct {
Iid sql.NullInt64 Iid sql.NullInt64
Name sql.NullString Name string
Vcenter sql.NullString Vcenter string
VmId sql.NullString VmId sql.NullString
EventKey sql.NullString EventKey sql.NullString
EventId sql.NullString EventId sql.NullString
CreationTime sql.NullString CreationTime sql.NullInt64
DeletionTime sql.NullString DeletionTime sql.NullInt64
ResourcePool sql.NullString ResourcePool sql.NullString
VmType sql.NullString VmType sql.NullString
Datacenter sql.NullString Datacenter sql.NullString
@@ -31,8 +31,8 @@ type Inventory struct {
type Updates struct { type Updates struct {
Uid sql.NullInt64 Uid sql.NullInt64
InventoryId sql.NullInt64 InventoryId sql.NullInt64
UpdateTime sql.NullString UpdateTime sql.NullInt64
UpdateType sql.NullString UpdateType string
NewVcpus sql.NullInt64 NewVcpus sql.NullInt64
NewRam sql.NullInt64 NewRam sql.NullInt64
NewResourcePool sql.NullString NewResourcePool sql.NullString

View File

@@ -20,12 +20,12 @@ RETURNING Iid, Name, Vcenter, VmId, EventKey, EventId, CreationTime, DeletionTim
` `
type CreateInventoryParams struct { type CreateInventoryParams struct {
Name sql.NullString Name string
Vcenter sql.NullString Vcenter string
VmId sql.NullString VmId sql.NullString
EventKey sql.NullString EventKey sql.NullString
EventId sql.NullString EventId sql.NullString
CreationTime sql.NullString CreationTime sql.NullInt64
ResourcePool sql.NullString ResourcePool sql.NullString
VmType sql.NullString VmType sql.NullString
Datacenter sql.NullString Datacenter sql.NullString
@@ -83,7 +83,7 @@ SELECT Iid, Name, Vcenter, VmId, EventKey, EventId, CreationTime, DeletionTime,
WHERE "Name" = ? LIMIT 1 WHERE "Name" = ? LIMIT 1
` `
func (q *Queries) GetInventoryByName(ctx context.Context, name sql.NullString) (Inventory, error) { func (q *Queries) GetInventoryByName(ctx context.Context, name string) (Inventory, error) {
row := q.db.QueryRowContext(ctx, getInventoryByName, name) row := q.db.QueryRowContext(ctx, getInventoryByName, name)
var i Inventory var i Inventory
err := row.Scan( err := row.Scan(

View File

@@ -10,11 +10,11 @@ func New(level Level, output Output) *slog.Logger {
var h slog.Handler var h slog.Handler
switch output { switch output {
case OutputJson: case OutputJson:
h = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: level.ToSlog()}) h = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: level.ToSlog(), AddSource: true})
case OutputText: case OutputText:
h = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level.ToSlog()}) h = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level.ToSlog(), AddSource: true})
default: default:
h = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level.ToSlog()}) h = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level.ToSlog(), AddSource: true})
} }
return slog.New(h) return slog.New(h)
} }

View File

@@ -40,13 +40,6 @@ func main() {
} }
defer database.Close() defer database.Close()
/*
if err = db.Migrate(database); err != nil && !errors.Is(err, migrate.ErrNoChange) {
logger.Error("failed to migrate database", "error", err)
return
}
*/
if err = db.Migrate(database); err != nil { if err = db.Migrate(database); err != nil {
logger.Error("failed to migrate database", "error", err) logger.Error("failed to migrate database", "error", err)
return return

View File

@@ -1,21 +1,73 @@
package handler package handler
import ( import (
"context"
"database/sql"
"encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"time"
queries "vctp/db/queries"
models "vctp/server/models"
) )
// VmCreate receives the CloudEvent for a VM creation // VmCreate receives the CloudEvent for a VM creation
func (h *Handler) VmCreate(w http.ResponseWriter, r *http.Request) { func (h *Handler) VmCreate(w http.ResponseWriter, r *http.Request) {
var unixTimestamp int64
reqBody, err := io.ReadAll(r.Body) reqBody, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
fmt.Fprintf(w, "Invalid data received") fmt.Fprintf(w, "Invalid data received")
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} else {
h.Logger.Debug("received input data", "length", len(reqBody))
}
// Decode the JSON body into vmModel struct
var vm models.CloudEventReceived
//if err := json.NewDecoder(r.Body).Decode(&vm); err != nil {
if err := json.Unmarshal(reqBody, &vm); err != nil {
h.Logger.Error("unable to decode json", "error", err)
http.Error(w, "Invalid JSON body", http.StatusBadRequest)
return
}
// Convert vmModel to CreateInventoryParams using the utility function
//var params queries.CreateInventoryParams
//db.ConvertToSQLParams(&vm, &params)
// Parse the datetime string to a time.Time object
eventTime, err := time.Parse(time.RFC3339, vm.CloudEvent.Time)
if err != nil {
h.Logger.Warn("unable to convert cloud event time to timestamp", "error", err)
unixTimestamp = time.Now().Unix()
} else {
// Convert to Unix timestamp
unixTimestamp = eventTime.Unix()
}
// Create an instance of CreateInventoryParams
params := queries.CreateInventoryParams{
Name: vm.CloudEvent.Data.VM.Name,
Vcenter: vm.CloudEvent.Source,
VmId: sql.NullString{String: "VirtualMachine-" + vm.CloudEvent.Data.VM.VM.Value, Valid: vm.CloudEvent.Data.VM.VM.Value != ""},
CreationTime: sql.NullInt64{Int64: unixTimestamp, Valid: unixTimestamp > 0},
}
h.Logger.Debug("database params", "vm", vm)
// Insert the new inventory record into the database
result, err := h.Database.Queries().CreateInventory(context.Background(), params)
if err != nil {
h.Logger.Error("unable to perform database insert", "error", err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Error : %v\n", err)
return
} }
h.Logger.Debug("received create request", "body", string(reqBody)) h.Logger.Debug("received create request", "body", string(reqBody))
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Create Request (%d): %v\n", len(reqBody), string(reqBody)) fmt.Fprintf(w, "Create Request : %v\n", result)
} }

60
server/models/models.go Normal file
View File

@@ -0,0 +1,60 @@
package models
import "time"
type CloudEventReceived struct {
CloudEvent struct {
ID string `json:"id"`
Specversion string `json:"specversion"`
Source string `json:"source"`
Type string `json:"type"`
Time string `json:"time"`
Data struct {
ChainID int `json:"ChainId"`
ChangeTag string `json:"ChangeTag"`
ComputeResource struct {
ComputeResource struct {
Type string `json:"Type"`
Value string `json:"Value"`
} `json:"ComputeResource"`
Name string `json:"Name"`
} `json:"ComputeResource"`
CreatedTime time.Time `json:"CreatedTime"`
Datacenter struct {
Datacenter struct {
Type string `json:"Type"`
Value string `json:"Value"`
} `json:"Datacenter"`
Name string `json:"Name"`
} `json:"Datacenter"`
Ds interface{} `json:"Ds"`
Dvs interface{} `json:"Dvs"`
FullFormattedMessage string `json:"FullFormattedMessage"`
Host struct {
Host struct {
Type string `json:"Type"`
Value string `json:"Value"`
} `json:"Host"`
Name string `json:"Name"`
} `json:"Host"`
Key int `json:"Key"`
Net interface{} `json:"Net"`
SrcTemplate struct {
Name string `json:"Name"`
VM struct {
Type string `json:"Type"`
Value string `json:"Value"`
} `json:"Vm"`
} `json:"SrcTemplate"`
Template bool `json:"Template"`
UserName string `json:"UserName"`
VM struct {
Name string `json:"Name"`
VM struct {
Type string `json:"Type"`
Value string `json:"Value"`
} `json:"Vm"`
} `json:"Vm"`
} `json:"data"`
} `json:"cloudEvent"`
}

View File

@@ -109,7 +109,7 @@ func (s *Server) Start() {
} }
} else { } else {
s.logger.Info("starting TLS server", "port", s.srv.Addr, "cert", s.tlsCertFilename, "key", s.tlsKeyFilename) s.logger.Info("starting TLS server", "port", s.srv.Addr, "cert", s.tlsCertFilename, "key", s.tlsKeyFilename)
if err := s.srv.ListenAndServeTLS(s.tlsCertFilename, s.tlsKeyFilename); err != nil { if err := s.srv.ListenAndServeTLS(s.tlsCertFilename, s.tlsKeyFilename); err != nil && err != http.ErrServerClosed {
s.logger.Warn("failed to start server", "error", err) s.logger.Warn("failed to start server", "error", err)
} }
} }