[ci skip] more suggested improvements

This commit is contained in:
2026-02-06 15:35:18 +11:00
parent dfbaacb6f3
commit 0e3cf5aae9
24 changed files with 452 additions and 356 deletions

View File

@@ -34,9 +34,9 @@ steps:
path: /shared
commands:
- export PATH=/drone/src/pkg.tools:$PATH
- go install github.com/a-h/templ/cmd/templ@latest
- go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
- go install github.com/swaggo/swag/cmd/swag@latest
- go install github.com/a-h/templ/cmd/templ@v0.3.977
- go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.29.0
- go install github.com/swaggo/swag/cmd/swag@v1.16.6
# - go install github.com/goreleaser/nfpm/v2/cmd/nfpm@latest
- sqlc generate
- templ generate -path ./components

View File

@@ -86,6 +86,40 @@ Hourly and daily snapshot table retention can be configured in the settings file
- `settings.hourly_snapshot_max_age_days` (default: 60)
- `settings.daily_snapshot_max_age_months` (default: 12)
## Runtime Environment Flags
These flags are read from the process environment (for example via `/etc/default/vctp` on systemd installs):
- `VCTP_ENCRYPTION_KEY`: optional explicit key source for credential encryption/decryption.
Recommended for stable behavior across host migrations/rebuilds.
- `VCTP_ENABLE_EXPERIMENTAL_POSTGRES`: set to `1` to enable experimental PostgreSQL driver startup.
- `VCTP_ENABLE_LEGACY_API`: set to `1` to re-enable deprecated legacy API endpoints temporarily.
## Credential Encryption Lifecycle
At startup, vCTP resolves `settings.vcenter_password` using this order:
1. If value starts with `enc:v1:`, decrypt using the active key.
2. If no prefix, attempt legacy ciphertext decryption (active key, then legacy fallback keys).
3. If decrypt fails and value length is greater than 2, treat value as plaintext.
When steps 2 or 3 succeed, vCTP rewrites the setting in-place to `enc:v1:<ciphertext>`.
Behavior notes:
- Plaintext values with length `<= 2` are rejected.
- Malformed ciphertext is rejected safely (short payloads do not panic).
- Legacy encrypted values can still be migrated forward automatically.
## Deprecated API Endpoints
These endpoints are considered legacy and are disabled by default unless `VCTP_ENABLE_LEGACY_API=1`:
- `/api/event/vm/create`
- `/api/event/vm/modify`
- `/api/event/vm/move`
- `/api/event/vm/delete`
- `/api/cleanup/updates`
- `/api/cleanup/vcenter`
When disabled, they return HTTP `410 Gone` with JSON error payload.
## Settings Reference
All configuration lives under the top-level `settings:` key in `vctp.yml`.
@@ -94,7 +128,7 @@ General:
- `settings.log_output`: log format, `text` or `json`
Database:
- `settings.database_driver`: `sqlite` or `postgres`
- `settings.database_driver`: `sqlite` or `postgres` (experimental; requires `VCTP_ENABLE_EXPERIMENTAL_POSTGRES=1`)
- `settings.database_url`: SQLite file path/DSN or PostgreSQL DSN
HTTP/TLS:
@@ -138,9 +172,9 @@ Filters/chargeback:
## Pre-requisite tools
```shell
go install github.com/a-h/templ/cmd/templ@latest
go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
go install github.com/swaggo/swag/cmd/swag@latest
go install github.com/a-h/templ/cmd/templ@v0.3.977
go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.29.0
go install github.com/swaggo/swag/cmd/swag@v1.16.6
```
## Database
@@ -163,6 +197,19 @@ Run `templ generate -path ./components` to generate code based on template files
## Documentation
Run `swag init --exclude "pkg.mod,pkg.build,pkg.tools" -o server/router/docs`
## Tests
Run the test suite:
```shell
go test ./...
```
Recommended static analysis:
```shell
go vet ./...
```
## CI/CD (Drone)
- `.drone.yml` defines a Docker pipeline:
- Restore/build caches for Go modules/tools.

View File

@@ -3,7 +3,6 @@ package handler
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"net/http"
"strings"
@@ -199,7 +198,5 @@ LIMIT %d
MissingCreationPartialSamples: partialSamples,
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
writeJSON(w, http.StatusOK, response)
}

View File

@@ -57,9 +57,7 @@ func (h *Handler) EncryptData(w http.ResponseWriter, r *http.Request) {
}
h.Logger.Debug("encrypted plaintext payload", "input_length", len(plaintext))
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(map[string]string{
writeJSON(w, http.StatusOK, map[string]string{
"status": "OK",
"message": cipherText,
"prefixed": encryptedValuePrefixV1 + cipherText,

View File

@@ -0,0 +1,135 @@
package handler
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"vctp/internal/secrets"
)
func newEncryptTestHandler() (*Handler, *secrets.Secrets) {
logger := newTestLogger()
key := []byte("0123456789abcdef0123456789abcdef")
secret := secrets.New(logger, key)
return &Handler{
Logger: logger,
Secret: secret,
}, secret
}
func decodeResponse(t *testing.T, rr *httptest.ResponseRecorder) map[string]string {
t.Helper()
var resp map[string]string
if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
t.Fatalf("failed to decode response body %q: %v", rr.Body.String(), err)
}
return resp
}
func TestEncryptDataRejectsWrongMethod(t *testing.T) {
h, _ := newEncryptTestHandler()
req := httptest.NewRequest(http.MethodGet, "/api/encrypt", nil)
rr := httptest.NewRecorder()
h.EncryptData(rr, req)
if rr.Code != http.StatusMethodNotAllowed {
t.Fatalf("expected %d, got %d", http.StatusMethodNotAllowed, rr.Code)
}
resp := decodeResponse(t, rr)
if resp["status"] != "ERROR" {
t.Fatalf("expected status ERROR, got %#v", resp)
}
}
func TestEncryptDataRejectsInvalidJSON(t *testing.T) {
h, _ := newEncryptTestHandler()
req := httptest.NewRequest(http.MethodPost, "/api/encrypt", strings.NewReader("{"))
rr := httptest.NewRecorder()
h.EncryptData(rr, req)
if rr.Code != http.StatusBadRequest {
t.Fatalf("expected %d, got %d", http.StatusBadRequest, rr.Code)
}
resp := decodeResponse(t, rr)
if resp["status"] != "ERROR" {
t.Fatalf("expected status ERROR, got %#v", resp)
}
}
func TestEncryptDataAcceptsPlaintextField(t *testing.T) {
h, secret := newEncryptTestHandler()
req := httptest.NewRequest(http.MethodPost, "/api/encrypt", strings.NewReader(`{"plaintext":"super-secret"}`))
rr := httptest.NewRecorder()
h.EncryptData(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("expected %d, got %d", http.StatusOK, rr.Code)
}
resp := decodeResponse(t, rr)
if resp["status"] != "OK" {
t.Fatalf("expected status OK, got %#v", resp)
}
if resp["ciphertext"] == "" || resp["prefixed"] == "" {
t.Fatalf("expected ciphertext+prefixed fields, got %#v", resp)
}
if !strings.HasPrefix(resp["prefixed"], encryptedValuePrefixV1) {
t.Fatalf("expected prefixed value with %q, got %q", encryptedValuePrefixV1, resp["prefixed"])
}
if !strings.EqualFold(resp["message"], resp["ciphertext"]) {
t.Fatalf("expected message to mirror ciphertext, got %#v", resp)
}
plain, err := secret.Decrypt(resp["ciphertext"])
if err != nil {
t.Fatalf("unable to decrypt ciphertext response: %v", err)
}
if string(plain) != "super-secret" {
t.Fatalf("unexpected decrypted value %q", string(plain))
}
}
func TestEncryptDataAcceptsLegacyValueField(t *testing.T) {
h, secret := newEncryptTestHandler()
body := bytes.NewBufferString(`{"value":"legacy-input"}`)
req := httptest.NewRequest(http.MethodPost, "/api/encrypt", body)
rr := httptest.NewRecorder()
h.EncryptData(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("expected %d, got %d", http.StatusOK, rr.Code)
}
resp := decodeResponse(t, rr)
cipherText := resp["ciphertext"]
if cipherText == "" {
t.Fatalf("expected ciphertext in response, got %#v", resp)
}
plain, err := secret.Decrypt(cipherText)
if err != nil {
t.Fatalf("unable to decrypt ciphertext response: %v", err)
}
if string(plain) != "legacy-input" {
t.Fatalf("unexpected decrypted value %q", string(plain))
}
}
func TestEncryptDataRejectsMissingPayloadValue(t *testing.T) {
h, _ := newEncryptTestHandler()
req := httptest.NewRequest(http.MethodPost, "/api/encrypt", strings.NewReader(`{}`))
rr := httptest.NewRecorder()
h.EncryptData(rr, req)
if rr.Code != http.StatusBadRequest {
t.Fatalf("expected %d, got %d", http.StatusBadRequest, rr.Code)
}
resp := decodeResponse(t, rr)
if resp["status"] != "ERROR" {
t.Fatalf("expected status ERROR, got %#v", resp)
}
}

View File

@@ -0,0 +1,63 @@
package handler
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestDenyLegacyAPIDisabledByDefault(t *testing.T) {
t.Setenv(legacyAPIEnvVar, "")
h := &Handler{Logger: newTestLogger()}
rr := httptest.NewRecorder()
denied := h.denyLegacyAPI(rr, "/api/event/vm/create")
if !denied {
t.Fatal("expected legacy API to be denied by default")
}
if rr.Code != http.StatusGone {
t.Fatalf("expected %d, got %d", http.StatusGone, rr.Code)
}
if !strings.Contains(rr.Body.String(), "deprecated") {
t.Fatalf("unexpected response body: %s", rr.Body.String())
}
}
func TestDenyLegacyAPIEnabledViaEnv(t *testing.T) {
t.Setenv(legacyAPIEnvVar, "1")
h := &Handler{Logger: newTestLogger()}
rr := httptest.NewRecorder()
denied := h.denyLegacyAPI(rr, "/api/event/vm/create")
if denied {
t.Fatal("expected legacy API to be allowed when env var is set")
}
if rr.Body.Len() != 0 {
t.Fatalf("expected no response body write, got: %s", rr.Body.String())
}
}
func TestVmCreateEventHonorsLegacyGate(t *testing.T) {
h := &Handler{Logger: newTestLogger()}
t.Run("disabled", func(t *testing.T) {
t.Setenv(legacyAPIEnvVar, "")
req := httptest.NewRequest(http.MethodPost, "/api/event/vm/create", strings.NewReader("{invalid"))
rr := httptest.NewRecorder()
h.VmCreateEvent(rr, req)
if rr.Code != http.StatusGone {
t.Fatalf("expected %d, got %d", http.StatusGone, rr.Code)
}
})
t.Run("enabled", func(t *testing.T) {
t.Setenv(legacyAPIEnvVar, "1")
req := httptest.NewRequest(http.MethodPost, "/api/event/vm/create", strings.NewReader("{invalid"))
rr := httptest.NewRecorder()
h.VmCreateEvent(rr, req)
if rr.Code != http.StatusBadRequest {
t.Fatalf("expected %d when gate is open, got %d", http.StatusBadRequest, rr.Code)
}
})
}

View File

@@ -2,7 +2,6 @@ package handler
import (
"context"
"encoding/json"
"fmt"
"net/http"
"vctp/internal/report"
@@ -24,12 +23,7 @@ func (h *Handler) InventoryReportDownload(w http.ResponseWriter, r *http.Request
reportData, err := report.CreateInventoryReport(h.Logger, h.Database, ctx)
if err != nil {
h.Logger.Error("Failed to create report", "error", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Unable to create xlsx report: '%s'", err),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Unable to create xlsx report: '%s'", err))
return
}
@@ -58,12 +52,7 @@ func (h *Handler) UpdateReportDownload(w http.ResponseWriter, r *http.Request) {
reportData, err := report.CreateUpdatesReport(h.Logger, h.Database, ctx)
if err != nil {
h.Logger.Error("Failed to create report", "error", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Unable to create xlsx report: '%s'", err),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Unable to create xlsx report: '%s'", err))
return
}

View File

@@ -0,0 +1,41 @@
package handler
import (
"encoding/json"
"net/http"
"vctp/server/models"
)
func writeJSON(w http.ResponseWriter, statusCode int, payload any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode)
_ = json.NewEncoder(w).Encode(payload)
}
func writeJSONStatus(w http.ResponseWriter, statusCode int, status string) {
writeJSON(w, statusCode, models.StatusResponse{
Status: status,
})
}
func writeJSONStatusMessage(w http.ResponseWriter, statusCode int, status, message string) {
writeJSON(w, statusCode, models.StatusMessageResponse{
Status: status,
Message: message,
})
}
func writeJSONOK(w http.ResponseWriter) {
writeJSONStatus(w, http.StatusOK, "OK")
}
func writeJSONOKMessage(w http.ResponseWriter, message string) {
writeJSONStatusMessage(w, http.StatusOK, "OK", message)
}
func writeJSONError(w http.ResponseWriter, statusCode int, message string) {
writeJSON(w, statusCode, models.ErrorResponse{
Status: "ERROR",
Message: message,
})
}

View File

@@ -2,13 +2,11 @@ package handler
import (
"context"
"encoding/json"
"net/http"
"strings"
"time"
"vctp/internal/settings"
"vctp/internal/tasks"
"vctp/server/models"
)
// SnapshotAggregateForce forces regeneration of a daily or monthly summary table.
@@ -102,18 +100,5 @@ func (h *Handler) SnapshotAggregateForce(w http.ResponseWriter, r *http.Request)
"granularity", granularity,
"duration", time.Since(startedAt),
)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "OK",
})
}
func writeJSONError(w http.ResponseWriter, status int, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(models.ErrorResponse{
Status: "ERROR",
Message: message,
})
writeJSONOK(w)
}

View File

@@ -2,7 +2,6 @@ package handler
import (
"context"
"encoding/json"
"net/http"
"strings"
"time"
@@ -44,8 +43,5 @@ func (h *Handler) SnapshotForceHourly(w http.ResponseWriter, r *http.Request) {
}
h.Logger.Info("Manual hourly snapshot completed", "duration", time.Since(started))
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"status": "OK",
})
writeJSONOK(w)
}

View File

@@ -2,9 +2,9 @@ package handler
import (
"context"
"encoding/json"
"net/http"
"vctp/internal/report"
"vctp/server/models"
)
// SnapshotMigrate rebuilds the snapshot registry and normalizes hourly table names.
@@ -19,20 +19,28 @@ func (h *Handler) SnapshotMigrate(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
stats, err := report.MigrateSnapshotRegistry(ctx, h.Database)
if err != nil {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "ERROR",
"error": err.Error(),
"stats": stats,
writeJSON(w, http.StatusInternalServerError, models.SnapshotMigrationResponse{
Status: "ERROR",
Error: err.Error(),
Stats: models.SnapshotMigrationStats{
HourlyRenamed: stats.HourlyRenamed,
HourlyRegistered: stats.HourlyRegistered,
DailyRegistered: stats.DailyRegistered,
MonthlyRegistered: stats.MonthlyRegistered,
Errors: stats.Errors,
},
})
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "OK",
"stats": stats,
writeJSON(w, http.StatusOK, models.SnapshotMigrationResponse{
Status: "OK",
Stats: models.SnapshotMigrationStats{
HourlyRenamed: stats.HourlyRenamed,
HourlyRegistered: stats.HourlyRegistered,
DailyRegistered: stats.DailyRegistered,
MonthlyRegistered: stats.MonthlyRegistered,
Errors: stats.Errors,
},
})
}

View File

@@ -1,13 +1,13 @@
package handler
import (
"encoding/json"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"vctp/internal/report"
"vctp/server/models"
)
// SnapshotRegenerateHourlyReports regenerates missing hourly snapshot XLSX reports on disk.
@@ -54,15 +54,14 @@ func (h *Handler) SnapshotRegenerateHourlyReports(w http.ResponseWriter, r *http
regenerated++
}
resp := map[string]interface{}{
"status": "OK",
"total": len(records),
"regenerated": regenerated,
"skipped": skipped,
"errors": errors,
"reports_dir": reportsDir,
"snapshotType": "hourly",
resp := models.SnapshotRegenerateReportsResponse{
Status: "OK",
Total: len(records),
Regenerated: regenerated,
Skipped: skipped,
Errors: errors,
ReportsDir: reportsDir,
SnapshotType: "hourly",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
writeJSON(w, http.StatusOK, resp)
}

View File

@@ -2,7 +2,6 @@ package handler
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strconv"
@@ -10,6 +9,7 @@ import (
"time"
"vctp/db"
"vctp/internal/report"
"vctp/server/models"
)
// SnapshotRepair scans existing daily summaries and backfills missing SnapshotTime and lifecycle fields.
@@ -21,20 +21,18 @@ import (
// @Router /api/snapshots/repair [post]
func (h *Handler) SnapshotRepair(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
writeJSONError(w, http.StatusMethodNotAllowed, "method not allowed")
return
}
h.Logger.Info("snapshot repair started", "scope", "daily")
repaired, failed := h.repairDailySummaries(r.Context(), time.Now())
h.Logger.Info("snapshot repair finished", "daily_repaired", repaired, "daily_failed", failed)
resp := map[string]string{
"status": "ok",
"repaired": strconv.Itoa(repaired),
"failed": strconv.Itoa(failed),
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(resp)
writeJSON(w, http.StatusOK, models.SnapshotRepairResponse{
Status: "OK",
Repaired: strconv.Itoa(repaired),
Failed: strconv.Itoa(failed),
})
}
func (h *Handler) repairDailySummaries(ctx context.Context, now time.Time) (repaired int, failed int) {
@@ -106,7 +104,7 @@ func (h *Handler) repairDailySummaries(ctx context.Context, now time.Time) (repa
// @Router /api/snapshots/repair/all [post]
func (h *Handler) SnapshotRepairSuite(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
writeJSONError(w, http.StatusMethodNotAllowed, "method not allowed")
return
}
@@ -135,15 +133,13 @@ func (h *Handler) SnapshotRepairSuite(w http.ResponseWriter, r *http.Request) {
h.Logger.Info("repair suite step", "step", "monthly_refine")
monthlyRefined, monthlyFailed := h.refineMonthlyFromDaily(ctx, time.Now())
resp := map[string]string{
"status": "ok",
"daily_repaired": strconv.Itoa(dailyRepaired),
"daily_failed": strconv.Itoa(dailyFailed),
"monthly_refined": strconv.Itoa(monthlyRefined),
"monthly_failed": strconv.Itoa(monthlyFailed),
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(resp)
writeJSON(w, http.StatusOK, models.SnapshotRepairSuiteResponse{
Status: "OK",
DailyRepaired: strconv.Itoa(dailyRepaired),
DailyFailed: strconv.Itoa(dailyFailed),
MonthlyRefined: strconv.Itoa(monthlyRefined),
MonthlyFailed: strconv.Itoa(monthlyFailed),
})
}
func (h *Handler) refineMonthlyFromDaily(ctx context.Context, now time.Time) (refined int, failed int) {

View File

@@ -2,7 +2,6 @@ package handler
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
@@ -62,24 +61,14 @@ func (h *Handler) SnapshotReportDownload(w http.ResponseWriter, r *http.Request)
ctx := context.Background()
tableName := r.URL.Query().Get("table")
if tableName == "" {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": "Missing table parameter",
})
writeJSONError(w, http.StatusBadRequest, "Missing table parameter")
return
}
reportData, err := report.CreateTableReport(h.Logger, h.Database, ctx, tableName)
if err != nil {
h.Logger.Error("Failed to create snapshot report", "error", err, "table", tableName)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Unable to create snapshot report: '%s'", err),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Unable to create snapshot report: '%s'", err))
return
}

View File

@@ -0,0 +1,10 @@
package handler
import (
"io"
"log/slog"
)
func newTestLogger() *slog.Logger {
return slog.New(slog.NewTextHandler(io.Discard, nil))
}

View File

@@ -43,12 +43,9 @@ func (h *Handler) UpdateCleanup(w http.ResponseWriter, r *http.Request) {
if err != nil {
h.Logger.Error("Error received cleaning updates table", "error", err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Delete Request unsuccessful %s\n", err)
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Delete Request unsuccessful %s", err))
} else {
h.Logger.Debug("Processed update cleanup successfully")
w.WriteHeader(http.StatusOK)
// TODO - return some JSON
fmt.Fprintf(w, "Processed update cleanup successfully")
writeJSONOKMessage(w, "Processed update cleanup successfully")
}
}

View File

@@ -3,7 +3,6 @@ package handler
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"net/http"
@@ -35,21 +34,11 @@ func (h *Handler) VcCleanup(w http.ResponseWriter, r *http.Request) {
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
h.Logger.Error("No VMs found for vcenter", "url", vcUrl)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("No match to vcenter details specified. vc_url: '%s'", vcUrl),
})
writeJSONError(w, http.StatusBadRequest, fmt.Sprintf("No match to vcenter details specified. vc_url: '%s'", vcUrl))
return
} else {
h.Logger.Error("Error checking for vcenter to cleanup", "error", err, "url", vcUrl)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Error checking for vcenter to cleanup. error: '%s'", err),
})
writeJSONError(w, http.StatusBadRequest, fmt.Sprintf("Error checking for vcenter to cleanup. error: '%s'", err))
return
}
} else {
@@ -57,33 +46,18 @@ func (h *Handler) VcCleanup(w http.ResponseWriter, r *http.Request) {
err = h.Database.Queries().InventoryCleanupVcenter(ctx, vcUrl)
if err != nil {
h.Logger.Error("Error cleaning up VMs from Inventory table", "error", err, "url", vcUrl)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Error cleaning up VMs from Inventory table. error: '%s'", err),
})
writeJSONError(w, http.StatusBadRequest, fmt.Sprintf("Error cleaning up VMs from Inventory table. error: '%s'", err))
return
} else {
// Successful cleanup
h.Logger.Debug("VMs successfully removed from inventory for vcenter", "url", vcUrl)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "OK",
"message": fmt.Sprintf("Removed VMs from Inventory table for vcenter '%s'", vcUrl),
})
writeJSONOKMessage(w, fmt.Sprintf("Removed VMs from Inventory table for vcenter '%s'", vcUrl))
return
}
}
} else {
h.Logger.Error("Parameters not correctly specified", "url", vcUrl)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Parameters not correctly specified. vc_url: '%s'", vcUrl),
})
writeJSONError(w, http.StatusBadRequest, fmt.Sprintf("Parameters not correctly specified. vc_url: '%s'", vcUrl))
return
}
}

View File

@@ -3,7 +3,6 @@ package handler
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"net/http"
@@ -38,21 +37,11 @@ func (h *Handler) VmCleanup(w http.ResponseWriter, r *http.Request) {
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
h.Logger.Error("No VM found matching parameters", "vm_id", vmId, "datacenter_name", datacenterName)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("No match to VM details specified. vm_id: '%s', datacenter_name: '%s'", vmId, datacenterName),
})
writeJSONError(w, http.StatusBadRequest, fmt.Sprintf("No match to VM details specified. vm_id: '%s', datacenter_name: '%s'", vmId, datacenterName))
return
} else {
h.Logger.Error("Error checking for VM to cleanup", "error", err, "vm_id", vmId, "datacenter_name", datacenterName)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Error checking for VM to cleanup. error: '%s'", err),
})
writeJSONError(w, http.StatusBadRequest, fmt.Sprintf("Error checking for VM to cleanup. error: '%s'", err))
return
}
} else {
@@ -68,33 +57,18 @@ func (h *Handler) VmCleanup(w http.ResponseWriter, r *http.Request) {
err = h.Database.Queries().InventoryCleanup(ctx, params)
if err != nil {
h.Logger.Error("Error cleaning up VM from Inventory table", "error", err, "vm_id", vmId, "datacenter_name", datacenterName)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Error cleaning up VM from Inventory table. error: '%s'", err),
})
writeJSONError(w, http.StatusBadRequest, fmt.Sprintf("Error cleaning up VM from Inventory table. error: '%s'", err))
return
} else {
// Successful cleanup
h.Logger.Debug("VM successfully removed from inventory", "vm_name", vm.Name, "iid", vm.Iid, "vm_id", vmId, "datacenter_name", datacenterName)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "OK",
"message": fmt.Sprintf("VM '%s' removed from Inventory table", vm.Name),
})
writeJSONOKMessage(w, fmt.Sprintf("VM '%s' removed from Inventory table", vm.Name))
return
}
}
} else {
h.Logger.Error("Parameters not correctly specified", "vm_id", vmId, "datacenter_name", datacenterName)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Parameters not correctly specified. vm_id: '%s', datacenter_name: '%s'", vmId, datacenterName),
})
writeJSONError(w, http.StatusBadRequest, fmt.Sprintf("Parameters not correctly specified. vm_id: '%s', datacenter_name: '%s'", vmId, datacenterName))
return
}
}

View File

@@ -20,11 +20,11 @@ import (
// @Tags events
// @Deprecated
// @Accept json
// @Produce text/plain
// @Produce json
// @Param event body models.CloudEventReceived true "CloudEvent payload"
// @Success 200 {string} string "Create event processed"
// @Failure 400 {string} string "Invalid request"
// @Failure 500 {string} string "Server error"
// @Success 200 {object} models.StatusMessageResponse "Create event processed"
// @Failure 400 {object} models.ErrorResponse "Invalid request"
// @Failure 500 {object} models.ErrorResponse "Server error"
// @Router /api/event/vm/create [post]
func (h *Handler) VmCreateEvent(w http.ResponseWriter, r *http.Request) {
if h.denyLegacyAPI(w, "/api/event/vm/create") {
@@ -41,8 +41,7 @@ func (h *Handler) VmCreateEvent(w http.ResponseWriter, r *http.Request) {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
h.Logger.Error("Invalid data received", "error", err)
fmt.Fprintf(w, "Invalid data received")
w.WriteHeader(http.StatusInternalServerError)
writeJSONError(w, http.StatusInternalServerError, "Invalid data received")
return
} else {
h.Logger.Debug("received input data", "length", len(reqBody))
@@ -52,7 +51,7 @@ func (h *Handler) VmCreateEvent(w http.ResponseWriter, r *http.Request) {
var event models.CloudEventReceived
if err := json.Unmarshal(reqBody, &event); err != nil {
h.Logger.Error("unable to decode json", "error", err)
http.Error(w, "Invalid JSON body", http.StatusBadRequest)
writeJSONError(w, http.StatusBadRequest, "Invalid JSON body")
return
} else {
h.Logger.Debug("successfully decoded JSON")
@@ -100,16 +99,13 @@ func (h *Handler) VmCreateEvent(w http.ResponseWriter, r *http.Request) {
result, err := h.Database.Queries().CreateEvent(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)
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Error: %v", err))
return
} else {
h.Logger.Debug("created database record", "insert_result", result)
}
//h.Logger.Debug("received create request", "body", string(reqBody))
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Create Request : %v\n", result)
writeJSONOKMessage(w, fmt.Sprintf("Create request processed: %v", result))
}
// prettyPrint comes from https://gist.github.com/sfate/9d45f6c5405dc4c9bf63bf95fe6d1a7c

View File

@@ -18,11 +18,11 @@ import (
// @Tags events
// @Deprecated
// @Accept json
// @Produce text/plain
// @Produce json
// @Param event body models.CloudEventReceived true "CloudEvent payload"
// @Success 200 {string} string "Delete event processed"
// @Failure 400 {string} string "Invalid request"
// @Failure 500 {string} string "Server error"
// @Success 200 {object} models.StatusMessageResponse "Delete event processed"
// @Failure 400 {object} models.ErrorResponse "Invalid request"
// @Failure 500 {object} models.ErrorResponse "Server error"
// @Router /api/event/vm/delete [post]
func (h *Handler) VmDeleteEvent(w http.ResponseWriter, r *http.Request) {
if h.denyLegacyAPI(w, "/api/event/vm/delete") {
@@ -36,8 +36,7 @@ func (h *Handler) VmDeleteEvent(w http.ResponseWriter, r *http.Request) {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
h.Logger.Error("Invalid data received", "error", err)
fmt.Fprintf(w, "Invalid data received")
w.WriteHeader(http.StatusInternalServerError)
writeJSONError(w, http.StatusInternalServerError, "Invalid data received")
return
} else {
//h.Logger.Debug("received input data", "length", len(reqBody))
@@ -48,7 +47,7 @@ func (h *Handler) VmDeleteEvent(w http.ResponseWriter, r *http.Request) {
if err := json.Unmarshal(reqBody, &event); err != nil {
h.Logger.Error("unable to decode json", "error", err)
prettyPrint(event)
http.Error(w, "Invalid JSON body", http.StatusBadRequest)
writeJSONError(w, http.StatusBadRequest, "Invalid JSON body")
return
} else {
h.Logger.Debug("successfully decoded deletion type cloud event", "vm_id", event.CloudEvent.Data.VM.VM.Value)
@@ -76,13 +75,10 @@ func (h *Handler) VmDeleteEvent(w http.ResponseWriter, r *http.Request) {
if err != nil {
h.Logger.Error("Error received marking VM as deleted", "error", err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Delete Request unsuccessful %s\n", err)
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Delete Request unsuccessful %s", err))
} else {
h.Logger.Debug("Processed VM Deletion event successfully")
w.WriteHeader(http.StatusOK)
// TODO - return some JSON
fmt.Fprintf(w, "Processed VM Deletion event successfully")
writeJSONOKMessage(w, "Processed VM Deletion event successfully")
}
//h.Logger.Debug("received delete request", "body", string(reqBody))

View File

@@ -29,12 +29,7 @@ func (h *Handler) VmImport(w http.ResponseWriter, r *http.Request) {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
h.Logger.Error("Invalid data received", "length", len(reqBody), "error", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Invalid data received: '%s'", err),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Invalid data received: '%s'", err))
return
} else {
@@ -45,12 +40,7 @@ func (h *Handler) VmImport(w http.ResponseWriter, r *http.Request) {
var inData models.ImportReceived
if err := json.Unmarshal(reqBody, &inData); err != nil {
h.Logger.Error("Unable to decode json request body", "length", len(reqBody), "error", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Unable to decode json request body: '%s'", err),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Unable to decode json request body: '%s'", err))
return
} else {
//h.Logger.Debug("successfully decoded JSON")
@@ -59,12 +49,7 @@ func (h *Handler) VmImport(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(inData.Name, "vCLS-") {
h.Logger.Info("Skipping internal vCLS VM import", "vm_name", inData.Name)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "OK",
"message": fmt.Sprintf("Skipped internal VM '%s'", inData.Name),
})
writeJSONOKMessage(w, fmt.Sprintf("Skipped internal VM '%s'", inData.Name))
return
}
@@ -103,10 +88,5 @@ func (h *Handler) VmImport(w http.ResponseWriter, r *http.Request) {
h.Logger.Info("not adding vm to inventory table since record alraedy exists", "vm_id", inData.VmId, "datacenter_name", inData.Datacenter)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "OK",
"message": fmt.Sprintf("Successfully processed import request for VM '%s'", inData.Name),
})
writeJSONOKMessage(w, fmt.Sprintf("Successfully processed import request for VM '%s'", inData.Name))
}

View File

@@ -46,12 +46,7 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
h.Logger.Error("Invalid data received", "length", len(reqBody), "error", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Invalid data received: '%s'", err),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Invalid data received: '%s'", err))
return
}
@@ -59,12 +54,7 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
var event models.CloudEventReceived
if err := json.Unmarshal(reqBody, &event); err != nil {
h.Logger.Error("Unable to decode json request body", "length", len(reqBody), "error", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Unable to decode json request body: '%s'", err),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Unable to decode json request body: '%s'", err))
return
} else {
//h.Logger.Debug("successfully decoded JSON")
@@ -74,12 +64,7 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
if event.CloudEvent.Data.ConfigChanges == nil {
h.Logger.Warn("Received event contains no config change")
prettyPrint(event)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
json.NewEncoder(w).Encode(map[string]string{
"status": "OK",
"message": "Received update event successfully but no config changes were found",
})
writeJSONStatusMessage(w, http.StatusAccepted, "OK", "Received update event successfully but no config changes were found")
return
} else {
h.Logger.Info("Received event contains config change info", "source", event.CloudEvent.Source,
@@ -239,13 +224,8 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
iid, err2 := h.AddVmToInventory(event, ctx, unixTimestamp)
if err2 != nil {
h.Logger.Error("Received error adding VM to inventory", "error", err2)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Valid request but experienced error adding vm id '%s' in datacenter name '%s' to inventory table : %s",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name, err2),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Valid request but experienced error adding vm id '%s' in datacenter name '%s' to inventory table : %s",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name, err2))
return
}
@@ -253,24 +233,14 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
params.InventoryId = sql.NullInt64{Int64: iid, Valid: iid > 0}
} else {
h.Logger.Error("Received zero for inventory id when adding VM to inventory")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Valid request but received zero result when adding vm id '%s' in datacenter name '%s' to inventory table",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Valid request but received zero result when adding vm id '%s' in datacenter name '%s' to inventory table",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name))
return
}
} else {
h.Logger.Error("unable to find existing inventory record for this VM", "error", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Valid request but could not locate vm id '%s' and datacenter name '%s' within inventory table : %s",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name, err),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Valid request but could not locate vm id '%s' and datacenter name '%s' within inventory table : %s",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name, err))
return
}
@@ -282,11 +252,7 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
if params.UpdateType == "diskChange" && invResult.ProvisionedDisk.Float64 == params.NewProvisionedDisk.Float64 {
h.Logger.Info("VM update type was for disk size but current size of VM matches inventory record, no need for update record",
"vm_name", invResult.Name, "db_value", invResult.ProvisionedDisk.Float64, "new_value", params.NewProvisionedDisk.Float64)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "OK",
"message": "Successfully processed vm modify event",
})
writeJSONOKMessage(w, "Successfully processed vm modify event")
return
}
@@ -303,23 +269,17 @@ func (h *Handler) VmModifyEvent(w http.ResponseWriter, r *http.Request) {
result, err := h.Database.Queries().CreateUpdate(ctx, 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)
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Error : %v", err))
return
} else {
h.Logger.Debug("created database record", "insert_result", result)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "OK",
"message": "Successfully processed vm modify event",
})
writeJSONOKMessage(w, "Successfully processed vm modify event")
return
}
} else {
h.Logger.Debug("Didn't find any configuration changes of interest", "id", event.CloudEvent.ID,
"vm", event.CloudEvent.Data.VM.Name, "config_changes", configChanges)
w.WriteHeader(http.StatusAccepted)
fmt.Fprintf(w, "Processed update event but no config changes were of interest\n")
writeJSONStatusMessage(w, http.StatusAccepted, "OK", "Processed update event but no config changes were of interest")
//prettyPrint(event.CloudEvent.Data.ConfigSpec)
}
}

View File

@@ -39,8 +39,7 @@ func (h *Handler) VmMoveEvent(w http.ResponseWriter, r *http.Request) {
reqBody, err := io.ReadAll(r.Body)
if err != nil {
h.Logger.Error("Invalid data received", "error", err)
fmt.Fprintf(w, "Invalid data received")
w.WriteHeader(http.StatusInternalServerError)
writeJSONError(w, http.StatusInternalServerError, "Invalid data received")
return
} else {
//h.Logger.Debug("received input data", "length", len(reqBody))
@@ -51,12 +50,7 @@ func (h *Handler) VmMoveEvent(w http.ResponseWriter, r *http.Request) {
if err := json.Unmarshal(reqBody, &event); err != nil {
h.Logger.Error("unable to unmarshal json", "error", err)
prettyPrint(reqBody)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Unable to unmarshal JSON in request body: '%s'", err),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Unable to unmarshal JSON in request body: '%s'", err))
return
} else {
h.Logger.Debug("successfully decoded JSON")
@@ -66,12 +60,7 @@ func (h *Handler) VmMoveEvent(w http.ResponseWriter, r *http.Request) {
if event.CloudEvent.Data.OldParent == nil || event.CloudEvent.Data.NewParent == nil {
h.Logger.Error("No resource pool data found in cloud event")
prettyPrint(event)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": "CloudEvent missing resource pool data",
})
writeJSONError(w, http.StatusBadRequest, "CloudEvent missing resource pool data")
return
}
@@ -92,13 +81,8 @@ func (h *Handler) VmMoveEvent(w http.ResponseWriter, r *http.Request) {
iid, err2 := h.AddVmToInventory(event, ctx, unixTimestamp)
if err2 != nil {
h.Logger.Error("Received error adding VM to inventory", "error", err2)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Valid request but experienced error adding vm id '%s' in datacenter name '%s' to inventory table : %s",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name, err2),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Valid request but experienced error adding vm id '%s' in datacenter name '%s' to inventory table : %s",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name, err2))
return
}
@@ -106,13 +90,8 @@ func (h *Handler) VmMoveEvent(w http.ResponseWriter, r *http.Request) {
params.InventoryId = sql.NullInt64{Int64: iid, Valid: iid > 0}
} else {
h.Logger.Error("Received zero for inventory id when adding VM to inventory")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Valid request but received zero result when adding vm id '%s' in datacenter name '%s' to inventory table",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Valid request but received zero result when adding vm id '%s' in datacenter name '%s' to inventory table",
event.CloudEvent.Data.VM.VM.Value, event.CloudEvent.Data.Datacenter.Name))
return
}
}
@@ -142,22 +121,12 @@ func (h *Handler) VmMoveEvent(w http.ResponseWriter, r *http.Request) {
result, err := h.Database.Queries().CreateUpdate(ctx, params)
if err != nil {
h.Logger.Error("unable to perform database insert", "error", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"status": "ERROR",
"message": fmt.Sprintf("Unable to insert move event into database: '%s'", err),
})
writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Unable to insert move event into database: '%s'", err))
return
} else {
h.Logger.Debug("created database record", "insert_result", result)
w.WriteHeader(http.StatusOK)
//fmt.Fprintf(w, "Processed update event: %v\n", result)
json.NewEncoder(w).Encode(map[string]string{
"status": "OK",
"message": "Successfully processed move event",
})
writeJSONOKMessage(w, "Successfully processed move event")
return
}
}

View File

@@ -3,7 +3,6 @@ package handler
import (
"context"
"database/sql"
"fmt"
"net/http"
"vctp/db/queries"
"vctp/internal/vcenter"
@@ -13,9 +12,9 @@ import (
// @Summary Refresh VM details
// @Description Queries vCenter and updates inventory records with missing details.
// @Tags inventory
// @Produce text/plain
// @Success 200 {string} string "Update completed"
// @Failure 500 {string} string "Server error"
// @Produce json
// @Success 200 {object} models.StatusMessageResponse "Update completed"
// @Failure 500 {object} models.ErrorResponse "Server error"
// @Router /api/inventory/vm/update [post]
func (h *Handler) VmUpdateDetails(w http.ResponseWriter, r *http.Request) {
var matchFound bool
@@ -42,8 +41,8 @@ func (h *Handler) VmUpdateDetails(w http.ResponseWriter, r *http.Request) {
results, err := h.Database.Queries().GetInventoryByVcenter(ctx, url)
if err != nil {
h.Logger.Error("Unable to query inventory table", "error", err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "Unable to query inventory table %s\n", err)
writeJSONError(w, http.StatusInternalServerError, "Unable to query inventory table")
return
}
if len(results) == 0 {
@@ -115,7 +114,5 @@ func (h *Handler) VmUpdateDetails(w http.ResponseWriter, r *http.Request) {
}
h.Logger.Debug("Processed vm update successfully")
w.WriteHeader(http.StatusOK)
// TODO - return some JSON
fmt.Fprintf(w, "Processed vm update successfully")
writeJSONOKMessage(w, "Processed vm update successfully")
}