more file structure
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1 +1,5 @@
|
|||||||
data.sqlite
|
data.sqlite
|
||||||
|
data.sqlite-journal
|
||||||
|
.env
|
||||||
|
vctp
|
||||||
|
vctp.log
|
43
api/resource/common/handler.go
Normal file
43
api/resource/common/handler.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Display some information about this API for the default page
|
||||||
|
// TODO - static fileserver with docs
|
||||||
|
func HomeLink(w http.ResponseWriter, r *http.Request) {
|
||||||
|
//w.WriteHeader(http.StatusNotImplemented)
|
||||||
|
fmt.Fprintf(w, "VM Chargeback Tracking Program. API interface only. See Nathan Coad (nathan.coad@dell.com) for further details. ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display error message for invalid requests
|
||||||
|
func HandleNotFound(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ip, err := IPFromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error", err)
|
||||||
|
http.Error(w, "VM Chargeback Tracking Program.. Invalid Path Specified.", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Request from IP %s\n", ip.String())
|
||||||
|
http.Error(w, "VM Chargeback Tracking Program. Invalid Path Specified.", http.StatusNotFound)
|
||||||
|
// TODO - investigate rate limiting for invalid requests
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPFromRequest extracts the user IP address from req, if present.
|
||||||
|
// @see https://blog.golang.org/context/userip/userip.go
|
||||||
|
func IPFromRequest(req *http.Request) (net.IP, error) {
|
||||||
|
ip, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
userIP := net.ParseIP(ip)
|
||||||
|
if userIP == nil {
|
||||||
|
return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)
|
||||||
|
}
|
||||||
|
return userIP, nil
|
||||||
|
}
|
32
api/resource/vm/handler.go
Normal file
32
api/resource/vm/handler.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO godoc
|
||||||
|
func VmCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
reqBody, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "Invalid data received")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debug("received create request", "body", string(reqBody))
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintf(w, "Create Request (%d): %v\n", len(reqBody), string(reqBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO godoc
|
||||||
|
func VmRemoveHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintf(w, "Remove Request: %v\n", vars)
|
||||||
|
}
|
58
api/resource/vm/model.go
Normal file
58
api/resource/vm/model.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package vm
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type CloudEventReceived struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Specversion string `json:"specversion"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Time time.Time `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"`
|
||||||
|
}
|
25
api/router/router.go
Normal file
25
api/router/router.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"vm-ctp/api/resource/common"
|
||||||
|
"vm-ctp/api/resource/vm"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New() *mux.Router {
|
||||||
|
r := mux.NewRouter()
|
||||||
|
|
||||||
|
// If nothing more specific is requested then just display some version information
|
||||||
|
r.HandleFunc("/", common.HomeLink)
|
||||||
|
|
||||||
|
s := r.PathPrefix("/api").Subrouter()
|
||||||
|
s.HandleFunc("/event/vm/create", vm.VmCreateHandler).Methods("POST") // receive VM creation event from Direktiv
|
||||||
|
s.HandleFunc("/event/vm/remove", vm.VmRemoveHandler).Methods("POST") // receive VM creation event from Direktiv
|
||||||
|
|
||||||
|
// Not found handler
|
||||||
|
r.NotFoundHandler = http.HandlerFunc(common.HandleNotFound)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
30
cert.pem
Normal file
30
cert.pem
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
9so3Qdv2RdPWtgdNX4EMDwxTUaeD6g==
|
||||||
|
-----END CERTIFICATE-----
|
157
cmd/api/main.go
157
cmd/api/main.go
@@ -1 +1,158 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
"vm-ctp/api/router"
|
||||||
|
utils "vm-ctp/internal/utils"
|
||||||
|
|
||||||
|
"github.com/gorilla/handlers"
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// For build numbers, from https://blog.kowalczyk.info/article/vEja/embedding-build-number-in-go-executable.html
|
||||||
|
var sha1ver string // sha1 revision used to build the program
|
||||||
|
var buildTime string // when the executable was built
|
||||||
|
var wait time.Duration
|
||||||
|
var bindDisableTls bool
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Load data from environment file
|
||||||
|
envFilename := utils.GetFilePath(".env")
|
||||||
|
err := godotenv.Load(envFilename)
|
||||||
|
if err != nil {
|
||||||
|
panic("Error loading .env file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open connection to logfile
|
||||||
|
// From https://ispycode.com/GO/Logging/Logging-to-multiple-destinations
|
||||||
|
logFile := os.Getenv("LOG_FILE")
|
||||||
|
if logFile == "" {
|
||||||
|
logFile = "./vctp.log"
|
||||||
|
}
|
||||||
|
logfileWriter, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Unable to write logfile '%s' : '%s'\n", logFile, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true})
|
||||||
|
slog.SetDefault(slog.New(h))
|
||||||
|
|
||||||
|
//log.SetOutput(logfileWriter)
|
||||||
|
//log.Printf("vCTP starting execution. Built on %s from sha1 %s. Runtime %s\n", buildTime, sha1ver, runtime.Version())
|
||||||
|
|
||||||
|
r := router.New()
|
||||||
|
|
||||||
|
// Log everything to stdout in Apache Common Log Format
|
||||||
|
var loggedRouter http.Handler
|
||||||
|
loggedRouter = handlers.LoggingHandler(logfileWriter, r)
|
||||||
|
|
||||||
|
// Set some options for TLS
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
||||||
|
PreferServerCipherSuites: true,
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
CipherSuites: []uint16{
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine bind IP
|
||||||
|
bindIP := os.Getenv("BIND_IP")
|
||||||
|
if bindIP == "" {
|
||||||
|
bindIP = utils.GetOutboundIP().String()
|
||||||
|
}
|
||||||
|
// Determine bind port
|
||||||
|
bindPort := os.Getenv("BIND_PORT")
|
||||||
|
if bindPort == "" {
|
||||||
|
bindPort = "9443"
|
||||||
|
}
|
||||||
|
bindAddress := fmt.Sprint(bindIP, ":", bindPort)
|
||||||
|
slog.Info("Will listen on address", "ip", bindIP, "port", bindPort)
|
||||||
|
|
||||||
|
// Determine bind disable TLS
|
||||||
|
bindDisableTlsEnv := os.Getenv("BIND_DISABLE_TLS")
|
||||||
|
if bindDisableTlsEnv == "true" {
|
||||||
|
bindDisableTls = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get file names for TLS cert/key
|
||||||
|
tlsCertFilename := os.Getenv("TLS_CERT_FILE")
|
||||||
|
if tlsCertFilename != "" {
|
||||||
|
tlsCertFilename = utils.GetFilePath(tlsCertFilename)
|
||||||
|
} else {
|
||||||
|
tlsCertFilename = "./cert.pem"
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsKeyFilename := os.Getenv("TLS_KEY_FILE")
|
||||||
|
if tlsKeyFilename != "" {
|
||||||
|
tlsKeyFilename = utils.GetFilePath(tlsKeyFilename)
|
||||||
|
} else {
|
||||||
|
tlsKeyFilename = "./privkey.pem"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate certificate if required
|
||||||
|
if !(utils.FileExists(tlsCertFilename) && utils.FileExists(tlsKeyFilename)) {
|
||||||
|
slog.Warn("Specified TLS certificate or private key do not exist", "certificate", tlsCertFilename, "tls-key", tlsKeyFilename)
|
||||||
|
utils.GenerateCerts(tlsCertFilename, tlsKeyFilename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the http server
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: bindAddress,
|
||||||
|
// Good practice to set timeouts to avoid Slowloris attacks.
|
||||||
|
WriteTimeout: time.Second * 15,
|
||||||
|
ReadTimeout: time.Second * 15,
|
||||||
|
IdleTimeout: time.Second * 60,
|
||||||
|
Handler: loggedRouter, // Pass our instance of gorilla/mux in.
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
|
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
|
||||||
|
}
|
||||||
|
slog.Info("Started listening on", "bindAddress", bindAddress)
|
||||||
|
|
||||||
|
// Run our server in a goroutine so that it doesn't block
|
||||||
|
// Also decide whether to start a TLS or plain http server
|
||||||
|
go func() {
|
||||||
|
if bindDisableTls {
|
||||||
|
if err := srv.ListenAndServe(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := srv.ListenAndServeTLS(tlsCertFilename, tlsKeyFilename); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c := make(chan os.Signal, 1)
|
||||||
|
// We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
|
||||||
|
// SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
|
||||||
|
signal.Notify(c, os.Interrupt)
|
||||||
|
|
||||||
|
// Block until we receive our signal.
|
||||||
|
<-c
|
||||||
|
|
||||||
|
// Create a deadline to wait for.
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), wait)
|
||||||
|
defer cancel()
|
||||||
|
// Doesn't block if no connections, but will otherwise wait
|
||||||
|
// until the timeout deadline.
|
||||||
|
srv.Shutdown(ctx)
|
||||||
|
// Optionally, you could run srv.Shutdown in a goroutine and block on
|
||||||
|
// <-ctx.Done() if your application should wait for other services
|
||||||
|
// to finalize based on context cancellation.
|
||||||
|
slog.Info("shutting down")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
Binary file not shown.
13
go.mod
13
go.mod
@@ -1,3 +1,12 @@
|
|||||||
module vm-cbp
|
module vm-ctp
|
||||||
|
|
||||||
go 1.21.6
|
go 1.23.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/gorilla/handlers v1.5.2
|
||||||
|
github.com/gorilla/mux v1.8.1
|
||||||
|
github.com/joho/godotenv v1.5.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
10
go.sum
Normal file
10
go.sum
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
||||||
|
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
||||||
|
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||||
|
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||||
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
139
internal/utils/certOperations.go
Normal file
139
internal/utils/certOperations.go
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"log"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateCerts(tlsCert string, tlsKey string) {
|
||||||
|
// @see https://shaneutt.com/blog/golang-ca-and-signed-cert-go/
|
||||||
|
// @see https://golang.org/src/crypto/tls/generate_cert.go
|
||||||
|
validFrom := ""
|
||||||
|
validFor := 365 * 24 * time.Hour
|
||||||
|
isCA := true
|
||||||
|
|
||||||
|
// Get the hostname
|
||||||
|
hostname, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the directory exists
|
||||||
|
relativePath := filepath.Dir(tlsCert)
|
||||||
|
log.Printf("GenerateCerts relative path for file creation is '%s'\n", relativePath)
|
||||||
|
_, err = os.Stat(relativePath)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
log.Printf("Certificate path does not exist, creating %s before generating certificate\n", relativePath)
|
||||||
|
os.MkdirAll(relativePath, os.ModePerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a private key
|
||||||
|
priv, err := rsa.GenerateKey(rand.Reader, rsaBits)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to generate private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var notBefore time.Time
|
||||||
|
if len(validFrom) == 0 {
|
||||||
|
notBefore = time.Now()
|
||||||
|
} else {
|
||||||
|
notBefore, err = time.Parse("Jan 2 15:04:05 2006", validFrom)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to parse creation date: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notAfter := notBefore.Add(validFor)
|
||||||
|
|
||||||
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to generate serial number: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
template := x509.Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Organization: []string{"DTMS"},
|
||||||
|
},
|
||||||
|
NotBefore: notBefore,
|
||||||
|
NotAfter: notAfter,
|
||||||
|
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
template.DNSNames = append(template.DNSNames, hostname)
|
||||||
|
|
||||||
|
// Add in all the non-local IPs
|
||||||
|
ifaces, err := net.Interfaces()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error enumerating interfaces: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, i := range ifaces {
|
||||||
|
addrs, err := i.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Oops: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, address := range addrs {
|
||||||
|
// check the address type and if it is not a loopback then add it to the list
|
||||||
|
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||||
|
if ipnet.IP.To4() != nil {
|
||||||
|
template.IPAddresses = append(template.IPAddresses, ipnet.IP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isCA {
|
||||||
|
template.IsCA = true
|
||||||
|
template.KeyUsage |= x509.KeyUsageCertSign
|
||||||
|
}
|
||||||
|
|
||||||
|
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create certificate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certOut, err := os.Create(tlsCert)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to open %s for writing: %v", tlsCert, err)
|
||||||
|
}
|
||||||
|
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
||||||
|
log.Fatalf("Failed to write data to %s: %v", tlsCert, err)
|
||||||
|
}
|
||||||
|
if err := certOut.Close(); err != nil {
|
||||||
|
log.Fatalf("Error closing %s: %v", tlsCert, err)
|
||||||
|
}
|
||||||
|
log.Printf("wrote %s\n", tlsCert)
|
||||||
|
|
||||||
|
keyOut, err := os.OpenFile(tlsKey, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to open %s for writing: %v", tlsKey, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to marshal private key: %v", err)
|
||||||
|
}
|
||||||
|
if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
|
||||||
|
log.Fatalf("Failed to write data to %s: %v", tlsKey, err)
|
||||||
|
}
|
||||||
|
if err := keyOut.Close(); err != nil {
|
||||||
|
log.Fatalf("Error closing %s: %v", tlsKey, err)
|
||||||
|
}
|
||||||
|
log.Printf("wrote %s\n", tlsKey)
|
||||||
|
}
|
55
internal/utils/utils.go
Normal file
55
internal/utils/utils.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const rsaBits = 4096
|
||||||
|
|
||||||
|
func GetFilePath(path string) string {
|
||||||
|
// Check for empty filename
|
||||||
|
if len(path) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if filename exists
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist((err)) {
|
||||||
|
slog.Info("File '%s' not found, searching in same directory as binary", path)
|
||||||
|
// if not, check that it exists in the same directory as the currently executing binary
|
||||||
|
ex, err2 := os.Executable()
|
||||||
|
if err2 != nil {
|
||||||
|
slog.Error("Error determining binary path : '%s'", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
binaryPath := filepath.Dir(ex)
|
||||||
|
path = filepath.Join(binaryPath, path)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get preferred outbound ip of this machine
|
||||||
|
// @see https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go
|
||||||
|
func GetOutboundIP() net.IP {
|
||||||
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||||
|
|
||||||
|
return localAddr.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a file exists from https://stackoverflow.com/questions/12518876/how-to-check-if-a-file-exists-in-go
|
||||||
|
func FileExists(filename string) bool {
|
||||||
|
info, err := os.Stat(filename)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !info.IsDir()
|
||||||
|
}
|
52
privkey.pem
Normal file
52
privkey.pem
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
Lp15oQrT2tvdis2K06zLyv9LJUFsvA==
|
||||||
|
-----END PRIVATE KEY-----
|
@@ -14,20 +14,13 @@ type Vm struct {
|
|||||||
Vcenter sql.NullString
|
Vcenter sql.NullString
|
||||||
CreationTime sql.NullString
|
CreationTime sql.NullString
|
||||||
DeletionTime sql.NullString
|
DeletionTime sql.NullString
|
||||||
TinRpTime sql.NullFloat64
|
|
||||||
BronzeRpTime sql.NullFloat64
|
|
||||||
SilverRpTime sql.NullFloat64
|
|
||||||
GoldRpTime sql.NullFloat64
|
|
||||||
ResourcePool sql.NullString
|
ResourcePool sql.NullString
|
||||||
VmType sql.NullString
|
VmType sql.NullString
|
||||||
PoweredOnPct sql.NullInt64
|
|
||||||
Datacenter sql.NullString
|
Datacenter sql.NullString
|
||||||
Cluster sql.NullString
|
Cluster sql.NullString
|
||||||
Folder sql.NullString
|
Folder sql.NullString
|
||||||
ProvisionedDisk sql.NullFloat64
|
ProvisionedDisk sql.NullFloat64
|
||||||
InitialVcpus sql.NullInt64
|
InitialVcpus sql.NullInt64
|
||||||
AvgVcpus sql.NullFloat64
|
|
||||||
InitialRam sql.NullInt64
|
InitialRam sql.NullInt64
|
||||||
AvgRam sql.NullFloat64
|
|
||||||
SrmPlaceholder sql.NullInt64
|
SrmPlaceholder sql.NullInt64
|
||||||
}
|
}
|
||||||
|
@@ -12,22 +12,45 @@ import (
|
|||||||
|
|
||||||
const create = `-- name: Create :execresult
|
const create = `-- name: Create :execresult
|
||||||
insert into "vm" (
|
insert into "vm" (
|
||||||
"Name", "Vcenter"
|
"Name", "Vcenter", "CreationTime", "ResourcePool", "VmType", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "InitialVcpus", "InitialRam", "SrmPlaceholder"
|
||||||
)
|
)
|
||||||
values(?, ?)
|
values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateParams struct {
|
type CreateParams struct {
|
||||||
Name sql.NullString
|
Name sql.NullString
|
||||||
Vcenter sql.NullString
|
Vcenter sql.NullString
|
||||||
|
CreationTime sql.NullString
|
||||||
|
ResourcePool sql.NullString
|
||||||
|
VmType sql.NullString
|
||||||
|
Datacenter sql.NullString
|
||||||
|
Cluster sql.NullString
|
||||||
|
Folder sql.NullString
|
||||||
|
ProvisionedDisk sql.NullFloat64
|
||||||
|
InitialVcpus sql.NullInt64
|
||||||
|
InitialRam sql.NullInt64
|
||||||
|
SrmPlaceholder sql.NullInt64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) Create(ctx context.Context, arg CreateParams) (sql.Result, error) {
|
func (q *Queries) Create(ctx context.Context, arg CreateParams) (sql.Result, error) {
|
||||||
return q.db.ExecContext(ctx, create, arg.Name, arg.Vcenter)
|
return q.db.ExecContext(ctx, create,
|
||||||
|
arg.Name,
|
||||||
|
arg.Vcenter,
|
||||||
|
arg.CreationTime,
|
||||||
|
arg.ResourcePool,
|
||||||
|
arg.VmType,
|
||||||
|
arg.Datacenter,
|
||||||
|
arg.Cluster,
|
||||||
|
arg.Folder,
|
||||||
|
arg.ProvisionedDisk,
|
||||||
|
arg.InitialVcpus,
|
||||||
|
arg.InitialRam,
|
||||||
|
arg.SrmPlaceholder,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const get = `-- name: Get :one
|
const get = `-- name: Get :one
|
||||||
select Id, Name, Vcenter, CreationTime, DeletionTime, TinRpTime, BronzeRpTime, SilverRpTime, GoldRpTime, ResourcePool, VmType, PoweredOnPct, Datacenter, Cluster, Folder, ProvisionedDisk, InitialVcpus, AvgVcpus, InitialRam, AvgRam, SrmPlaceholder from "vm" where "Name" = ?
|
select Id, Name, Vcenter, CreationTime, DeletionTime, ResourcePool, VmType, Datacenter, Cluster, Folder, ProvisionedDisk, InitialVcpus, InitialRam, SrmPlaceholder from "vm" where "Name" = ?
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) Get(ctx context.Context, name sql.NullString) (Vm, error) {
|
func (q *Queries) Get(ctx context.Context, name sql.NullString) (Vm, error) {
|
||||||
@@ -39,28 +62,21 @@ func (q *Queries) Get(ctx context.Context, name sql.NullString) (Vm, error) {
|
|||||||
&i.Vcenter,
|
&i.Vcenter,
|
||||||
&i.CreationTime,
|
&i.CreationTime,
|
||||||
&i.DeletionTime,
|
&i.DeletionTime,
|
||||||
&i.TinRpTime,
|
|
||||||
&i.BronzeRpTime,
|
|
||||||
&i.SilverRpTime,
|
|
||||||
&i.GoldRpTime,
|
|
||||||
&i.ResourcePool,
|
&i.ResourcePool,
|
||||||
&i.VmType,
|
&i.VmType,
|
||||||
&i.PoweredOnPct,
|
|
||||||
&i.Datacenter,
|
&i.Datacenter,
|
||||||
&i.Cluster,
|
&i.Cluster,
|
||||||
&i.Folder,
|
&i.Folder,
|
||||||
&i.ProvisionedDisk,
|
&i.ProvisionedDisk,
|
||||||
&i.InitialVcpus,
|
&i.InitialVcpus,
|
||||||
&i.AvgVcpus,
|
|
||||||
&i.InitialRam,
|
&i.InitialRam,
|
||||||
&i.AvgRam,
|
|
||||||
&i.SrmPlaceholder,
|
&i.SrmPlaceholder,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = `-- name: List :many
|
const list = `-- name: List :many
|
||||||
select Id, Name, Vcenter, CreationTime, DeletionTime, TinRpTime, BronzeRpTime, SilverRpTime, GoldRpTime, ResourcePool, VmType, PoweredOnPct, Datacenter, Cluster, Folder, ProvisionedDisk, InitialVcpus, AvgVcpus, InitialRam, AvgRam, SrmPlaceholder from "vm" order by "Name"
|
select Id, Name, Vcenter, CreationTime, DeletionTime, ResourcePool, VmType, Datacenter, Cluster, Folder, ProvisionedDisk, InitialVcpus, InitialRam, SrmPlaceholder from "vm" order by "Name"
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) List(ctx context.Context) ([]Vm, error) {
|
func (q *Queries) List(ctx context.Context) ([]Vm, error) {
|
||||||
@@ -78,21 +94,14 @@ func (q *Queries) List(ctx context.Context) ([]Vm, error) {
|
|||||||
&i.Vcenter,
|
&i.Vcenter,
|
||||||
&i.CreationTime,
|
&i.CreationTime,
|
||||||
&i.DeletionTime,
|
&i.DeletionTime,
|
||||||
&i.TinRpTime,
|
|
||||||
&i.BronzeRpTime,
|
|
||||||
&i.SilverRpTime,
|
|
||||||
&i.GoldRpTime,
|
|
||||||
&i.ResourcePool,
|
&i.ResourcePool,
|
||||||
&i.VmType,
|
&i.VmType,
|
||||||
&i.PoweredOnPct,
|
|
||||||
&i.Datacenter,
|
&i.Datacenter,
|
||||||
&i.Cluster,
|
&i.Cluster,
|
||||||
&i.Folder,
|
&i.Folder,
|
||||||
&i.ProvisionedDisk,
|
&i.ProvisionedDisk,
|
||||||
&i.InitialVcpus,
|
&i.InitialVcpus,
|
||||||
&i.AvgVcpus,
|
|
||||||
&i.InitialRam,
|
&i.InitialRam,
|
||||||
&i.AvgRam,
|
|
||||||
&i.SrmPlaceholder,
|
&i.SrmPlaceholder,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@@ -6,6 +6,6 @@ select * from "vm" order by "Name";
|
|||||||
|
|
||||||
-- name: Create :execresult
|
-- name: Create :execresult
|
||||||
insert into "vm" (
|
insert into "vm" (
|
||||||
"Name", "Vcenter"
|
"Name", "Vcenter", "CreationTime", "ResourcePool", "VmType", "Datacenter", "Cluster", "Folder", "ProvisionedDisk", "InitialVcpus", "InitialRam", "SrmPlaceholder"
|
||||||
)
|
)
|
||||||
values(?, ?);
|
values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
|
@@ -4,20 +4,13 @@ CREATE TABLE IF NOT EXISTS "vm" (
|
|||||||
"Vcenter" TEXT,
|
"Vcenter" TEXT,
|
||||||
"CreationTime" TEXT,
|
"CreationTime" TEXT,
|
||||||
"DeletionTime" TEXT,
|
"DeletionTime" TEXT,
|
||||||
"TinRpTime" REAL,
|
|
||||||
"BronzeRpTime" REAL,
|
|
||||||
"SilverRpTime" REAL,
|
|
||||||
"GoldRpTime" REAL,
|
|
||||||
"ResourcePool" TEXT,
|
"ResourcePool" TEXT,
|
||||||
"VmType" TEXT,
|
"VmType" TEXT,
|
||||||
"PoweredOnPct" INTEGER,
|
|
||||||
"Datacenter" TEXT,
|
"Datacenter" TEXT,
|
||||||
"Cluster" TEXT,
|
"Cluster" TEXT,
|
||||||
"Folder" TEXT,
|
"Folder" TEXT,
|
||||||
"ProvisionedDisk" REAL,
|
"ProvisionedDisk" REAL,
|
||||||
"InitialVcpus" INTEGER,
|
"InitialVcpus" INTEGER,
|
||||||
"AvgVcpus" REAL,
|
|
||||||
"InitialRam" INTEGER,
|
"InitialRam" INTEGER,
|
||||||
"AvgRam" REAL,
|
|
||||||
"SrmPlaceholder" INTEGER
|
"SrmPlaceholder" INTEGER
|
||||||
);
|
);
|
Reference in New Issue
Block a user