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) } }