Initial commit: VMware vSphere API simulator scaffold.

Add the FastAPI app, PostgreSQL migrations, Docker/Helm packaging, API
contracts, docs, client examples, and the unit/integration/compatibility
test suite for local client and tooling labs without a real vCenter.
This commit is contained in:
2026-07-18 04:42:11 +03:00
commit f8d3cbdd59
422 changed files with 361335 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
module example.com/vmware-api-simulator-cookbook
go 1.22
+121
View File
@@ -0,0 +1,121 @@
package main
import (
"crypto/tls"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
)
func env(k, def string) string {
if v := os.Getenv(k); v != "" {
return v
}
return def
}
var client = &http.Client{
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}, // lab self-signed cert only
}
func main() {
base := strings.TrimRight(env("VSPHERE_BASE", "https://localhost"), "/")
user := env("VSPHERE_USER", "administrator@vsphere.local")
password := env("VSPHERE_PASSWORD", "VMware1!")
vmName := env("VSPHERE_VM_NAME", "go-lab-01")
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+password))
var sessionID string
call(base+"/api/session", "POST", map[string]string{"Authorization": auth}, nil, &sessionID)
fmt.Println("session:", sessionID)
headers := map[string]string{"vmware-api-session-id": sessionID}
var vms []map[string]any
call(base+"/api/vcenter/vm", "GET", headers, nil, &vms)
fmt.Printf("vms before: %d\n", len(vms))
body, _ := json.Marshal(map[string]any{
"name": vmName,
"guest_OS": "OTHER_GUEST_64",
"placement": map[string]string{
"folder": "group-v23",
"host": "host-11",
"datastore": "datastore-31",
"resource_pool": "resgroup-22",
},
"cpu": map[string]int{"count": 1},
"memory": map[string]int{"size_MiB": 512},
})
var moid string
call(base+"/api/vcenter/vm", "POST", headers, body, &moid)
fmt.Println("created:", moid)
var power map[string]string
call(base+"/api/vcenter/vm/"+moid+"/power?action=start", "POST", headers, nil, &power)
waitTask(base, headers, power["task"])
var detail map[string]any
call(base+"/api/vcenter/vm/"+moid, "GET", headers, nil, &detail)
fmt.Println("power_state:", detail["power_state"])
call(base+"/api/vcenter/vm/"+moid+"/power?action=stop", "POST", headers, nil, &power)
waitTask(base, headers, power["task"])
call(base+"/api/vcenter/vm/"+moid, "DELETE", headers, nil, nil)
call(base+"/api/session", "DELETE", headers, nil, nil)
fmt.Println("ok")
}
func waitTask(base string, headers map[string]string, task string) {
if task == "" {
return
}
deadline := time.Now().Add(2 * time.Minute)
for time.Now().Before(deadline) {
var status map[string]any
call(base+"/api/cis/tasks/"+task, "GET", headers, nil, &status)
if s, _ := status["status"].(string); s == "SUCCEEDED" || s == "FAILED" {
return
}
time.Sleep(300 * time.Millisecond)
}
panic("timeout waiting for task " + task)
}
func call(url, method string, headers map[string]string, body []byte, out any) {
var reqBody io.Reader
if body != nil {
reqBody = strings.NewReader(string(body))
}
req, err := http.NewRequest(method, url, reqBody)
if err != nil {
panic(err)
}
for k, v := range headers {
req.Header.Set(k, v)
}
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
raw, _ := io.ReadAll(resp.Body)
if resp.StatusCode >= 300 {
panic(fmt.Sprintf("%s %s: %d %s", method, url, resp.StatusCode, raw))
}
if out == nil || len(raw) == 0 {
return
}
if err := json.Unmarshal(raw, out); err != nil {
panic(err)
}
}