Initial commit: stateful OpenStack API laboratory simulator.
Ship Keystone auth, multi-service handlers (Yoga→Dalmatian), Compose/Helm packaging, API contract packs, and pytest/Pulumi coverage labs.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
module example.com/proxmox-api-simulator-cookbook
|
||||
|
||||
go 1.22
|
||||
@@ -0,0 +1,97 @@
|
||||
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", "http://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
|
||||
}
|
||||
Reference in New Issue
Block a user