feat: share proxmox cluster client with worker
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"forgejo.digital-droplets.de/philschlo/proxui/platform/cluster"
|
||||
)
|
||||
|
||||
func TestClientAcceptsMatchingFingerprintAndSendsTokenHeader(t *testing.T) {
|
||||
var authHeader string
|
||||
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
authHeader = r.Header.Get("Authorization")
|
||||
if r.URL.Path != "/api2/json/version" {
|
||||
t.Fatalf("path = %q, want /api2/json/version", r.URL.Path)
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"data":{"version":"8.2"}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := newTestClient(t, server, fingerprintForServer(server))
|
||||
response, err := client.Get(context.Background(), "/version")
|
||||
if err != nil {
|
||||
t.Fatalf("Get() error = %v", err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
t.Fatalf("status = %d, want %d", response.StatusCode, http.StatusOK)
|
||||
}
|
||||
if authHeader != "PVEAPIToken=root@pam!proxui=secret-token" {
|
||||
t.Fatalf("Authorization = %q", authHeader)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientRejectsMismatchedFingerprint(t *testing.T) {
|
||||
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := newTestClient(t, server, strings.Repeat("0", sha256.Size*2))
|
||||
_, err := client.Get(context.Background(), "/version")
|
||||
if err == nil {
|
||||
t.Fatal("Get() error = nil, want error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "fingerprint mismatch") {
|
||||
t.Fatalf("error = %q, want fingerprint mismatch", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientRetriesServerErrors(t *testing.T) {
|
||||
var calls int
|
||||
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
calls++
|
||||
if calls == 1 {
|
||||
http.Error(w, "temporary", http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := newTestClient(t, server, fingerprintForServer(server))
|
||||
response, err := client.Post(context.Background(), "/nodes/pve/status", []byte(`{"command":"start"}`))
|
||||
if err != nil {
|
||||
t.Fatalf("Post() error = %v", err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
_, _ = io.Copy(io.Discard, response.Body)
|
||||
|
||||
if response.StatusCode != http.StatusNoContent {
|
||||
t.Fatalf("status = %d, want %d", response.StatusCode, http.StatusNoContent)
|
||||
}
|
||||
if calls != 2 {
|
||||
t.Fatalf("calls = %d, want 2", calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientGetsTaskStatus(t *testing.T) {
|
||||
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/api2/json/nodes/pve/tasks/UPID:pve:1/status" {
|
||||
t.Fatalf("path = %q", r.URL.Path)
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"data":{"status":"stopped","exitstatus":"OK"}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := newTestClient(t, server, fingerprintForServer(server))
|
||||
status, err := client.GetTaskStatus(context.Background(), "pve", "UPID:pve:1")
|
||||
if err != nil {
|
||||
t.Fatalf("GetTaskStatus() error = %v", err)
|
||||
}
|
||||
|
||||
if status.Status != "stopped" {
|
||||
t.Fatalf("Status = %q, want stopped", status.Status)
|
||||
}
|
||||
if status.ExitStatus != "OK" {
|
||||
t.Fatalf("ExitStatus = %q, want OK", status.ExitStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClientRejectsInvalidFingerprint(t *testing.T) {
|
||||
_, err := NewClient(cluster.Cluster{
|
||||
APIEndpoint: "https://pve.example.test:8006/api2/json",
|
||||
TLSFingerprint: "invalid",
|
||||
TokenID: "root@pam!proxui",
|
||||
TokenSecret: "secret-token",
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("NewClient() error = nil, want error")
|
||||
}
|
||||
}
|
||||
|
||||
func newTestClient(t *testing.T, server *httptest.Server, fingerprint string) *Client {
|
||||
t.Helper()
|
||||
|
||||
rootCAs := x509.NewCertPool()
|
||||
rootCAs.AddCert(server.Certificate())
|
||||
|
||||
client, err := NewClient(cluster.Cluster{
|
||||
APIEndpoint: server.URL + "/api2/json",
|
||||
TLSFingerprint: fingerprint,
|
||||
TokenID: "root@pam!proxui",
|
||||
TokenSecret: "secret-token",
|
||||
}, WithRootCAs(rootCAs), WithTimeout(time.Second), WithRetries(1))
|
||||
if err != nil {
|
||||
t.Fatalf("NewClient() error = %v", err)
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
func fingerprintForServer(server *httptest.Server) string {
|
||||
sum := sha256.Sum256(server.Certificate().Raw)
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
Reference in New Issue
Block a user