Files
Sergey Antropoff 48df10b17e Prepare 0.1.0 for lab release: durable handlers, HTTP Compose, CI, and pulumi-tests.
- Harden DB-backed handlers and seed profiles; align client wire shapes for
  cluster resources, QEMU config, and node SSL fields
- Serve plain HTTP on Compose :8006; keep TLS optional (--profile tls) and
  terminate HTTPS at Kubernetes Ingress
- Add pulumi-tests (full contract surface majors 6–9 + BPG lifecycle) and
  make pulumi-tests
- Ship bilingual docs, CHANGELOG, SECURITY, CONTRIBUTING, and GitHub Actions
  (make ci + Compose/Helm validation)
2026-07-18 04:18:05 +03:00

98 lines
2.4 KiB
Go

package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
func env(k, def string) string {
if v := os.Getenv(k); v != "" {
return v
}
return def
}
func main() {
base := strings.TrimRight(env("PVE_BASE", "https://localhost:8006/api2/json"), "/")
node := env("PVE_NODE", "pve01")
vmid := env("PVE_VMID", "113")
token := env("PVE_API_TOKEN", "root@pam!automation=automation-secret")
auth := "PVEAPIToken=" + token
fmt.Printf("version: %v\n", call(base+"/version", "GET", auth, nil))
upid := asString(call(base+"/nodes/"+node+"/qemu", "POST", auth, url.Values{
"vmid": {vmid},
"name": {"go-" + vmid},
"cores": {"1"},
"memory": {"512"},
}))
wait(base, node, auth, upid)
upid = asString(call(base+"/nodes/"+node+"/qemu/"+vmid+"/status/start", "POST", auth, nil))
wait(base, node, auth, upid)
fmt.Printf("status: %v\n", call(base+"/nodes/"+node+"/qemu/"+vmid+"/status/current", "GET", auth, nil))
upid = asString(call(base+"/nodes/"+node+"/qemu/"+vmid+"/status/stop", "POST", auth, nil))
wait(base, node, auth, upid)
upid = asString(call(base+"/nodes/"+node+"/qemu/"+vmid, "DELETE", auth, nil))
wait(base, node, auth, upid)
fmt.Println("ok")
}
func wait(base, node, auth, upid string) {
deadline := time.Now().Add(2 * time.Minute)
for time.Now().Before(deadline) {
status := call(base+"/nodes/"+node+"/tasks/"+url.PathEscape(upid)+"/status", "GET", auth, nil)
if m, ok := status.(map[string]any); ok {
if s, _ := m["status"].(string); s == "stopped" {
return
}
}
time.Sleep(500 * time.Millisecond)
}
panic("timeout waiting for " + upid)
}
func call(u, method, auth string, values url.Values) any {
var body io.Reader
if values != nil {
body = strings.NewReader(values.Encode())
}
req, err := http.NewRequest(method, u, body)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", auth)
if values != nil {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
b, _ := io.ReadAll(resp.Body)
if resp.StatusCode >= 300 {
panic(fmt.Sprintf("%s %s: %s", method, u, b))
}
var envelope struct {
Data any `json:"data"`
}
if err := json.Unmarshal(b, &envelope); err != nil {
panic(err)
}
return envelope.Data
}
func asString(v any) string {
s, ok := v.(string)
if !ok {
panic(fmt.Sprintf("expected string UPID, got %#v", v))
}
return s
}