Repository: hetznercloud/hcloud-go
Pull request #134 (2020-05-11 20:37:11)

Add API for requesting a VNC console
Add API for requesting a WebSocket VNC console

See also: #113

LKaemmerling: Thank you this looks fine for me! Could you please add a line to the [CHANGES.md](https://github.com/hetznercloud/hcloud-go/blob/master/CHANGES.md)
minus7: Done

Patch:
diff --git a/CHANGES.md b/CHANGES.md
index 3cfdb70..cfbd8c9 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,6 +5,7 @@
 * Add `Status` field to `Volume`
 * Add subnet type `cloud`
 * Add `WithHTTPClient` option to specify a custom `http.Client`
+* Add API for requesting a VNC console
 
 ## v1.17.0
 
diff --git a/hcloud/schema/server.go b/hcloud/schema/server.go
index 32cd00f..836b3bf 100644
--- a/hcloud/schema/server.go
+++ b/hcloud/schema/server.go
@@ -314,6 +314,18 @@ type ServerActionChangeProtectionResponse struct {
 	Action Action `json:"action"`
 }
 
+// ServerActionRequestConsoleRequest defines the schema of the request to
+// request a WebSocket VNC console.
+type ServerActionRequestConsoleRequest struct{}
+
+// ServerActionRequestConsoleResponse defines the schema of the response when
+// requesting a WebSocket VNC console.
+type ServerActionRequestConsoleResponse struct {
+	Action   Action `json:"action"`
+	WSSURL   string `json:"wss_url"`
+	Password string `json:"password"`
+}
+
 // ServerActionAttachToNetworkRequest defines the schema for the request to
 // attach a network to a server.
 type ServerActionAttachToNetworkRequest struct {
diff --git a/hcloud/server.go b/hcloud/server.go
index 9653a25..5a122c8 100644
--- a/hcloud/server.go
+++ b/hcloud/server.go
@@ -820,6 +820,33 @@ func (c *ServerClient) ChangeProtection(ctx context.Context, server *Server, opt
 	return ActionFromSchema(respBody.Action), resp, err
 }
 
+// ServerRequestConsoleResult is the result of requesting a WebSocket VNC console.
+type ServerRequestConsoleResult struct {
+	Action   *Action
+	WSSURL   string
+	Password string
+}
+
+// RequestConsole requests a WebSocket VNC console.
+func (c *ServerClient) RequestConsole(ctx context.Context, server *Server) (ServerRequestConsoleResult, *Response, error) {
+	path := fmt.Sprintf("/servers/%d/actions/request_console", server.ID)
+	req, err := c.client.NewRequest(ctx, "POST", path, nil)
+	if err != nil {
+		return ServerRequestConsoleResult{}, nil, err
+	}
+
+	respBody := schema.ServerActionRequestConsoleResponse{}
+	resp, err := c.client.Do(req, &respBody)
+	if err != nil {
+		return ServerRequestConsoleResult{}, resp, err
+	}
+	return ServerRequestConsoleResult{
+		Action:   ActionFromSchema(respBody.Action),
+		WSSURL:   respBody.WSSURL,
+		Password: respBody.Password,
+	}, resp, nil
+}
+
 // ServerAttachToNetworkOpts specifies options for attaching a server to a network.
 type ServerAttachToNetworkOpts struct {
 	Network  *Network


Test patch:
diff --git a/hcloud/server_test.go b/hcloud/server_test.go
index eb58e01..bc1013a 100644
--- a/hcloud/server_test.go
+++ b/hcloud/server_test.go
@@ -1528,6 +1528,36 @@ func TestServerClientChangeProtection(t *testing.T) {
 	})
 }
 
+func TestServerClientRequestConsole(t *testing.T) {
+	env := newTestEnv()
+	defer env.Teardown()
+
+	env.Mux.HandleFunc("/servers/1/actions/request_console", func(w http.ResponseWriter, r *http.Request) {
+		json.NewEncoder(w).Encode(schema.ServerActionRequestConsoleResponse{
+			Action: schema.Action{
+				ID: 1,
+			},
+			WSSURL:   "wss://console.hetzner.cloud/?server_id=1&token=3db32d15-af2f-459c-8bf8-dee1fd05f49c",
+			Password: "9MQaTg2VAGI0FIpc10k3UpRXcHj2wQ6x",
+		})
+	})
+
+	ctx := context.Background()
+	result, _, err := env.Client.Server.RequestConsole(ctx, &Server{ID: 1})
+	if err != nil {
+		t.Fatal(err)
+	}
+	if result.Action.ID != 1 {
+		t.Errorf("unexpected action ID: %d", result.Action.ID)
+	}
+	if result.WSSURL != "wss://console.hetzner.cloud/?server_id=1&token=3db32d15-af2f-459c-8bf8-dee1fd05f49c" {
+		t.Errorf("unexpected WebSocket URL: %v", result.WSSURL)
+	}
+	if result.Password != "9MQaTg2VAGI0FIpc10k3UpRXcHj2wQ6x" {
+		t.Errorf("unexpected password: %v", result.Password)
+	}
+}
+
 func TestServerClientAttachToNetwork(t *testing.T) {
 	var (
 		ctx    = context.Background()