Repository: hetznercloud/hcloud-go (go)

Issue:
Implement enabling and disabling backups for a server
https://docs.hetzner.cloud/#resources-server-actions-post-11

https://docs.hetzner.cloud/#resources-server-actions-post-12

Pull request description:
Implement enabling and disabling backup
Closes #31.

Patch:
diff --git a/hcloud/schema/server.go b/hcloud/schema/server.go
index 1903b6e..3a5f695 100644
--- a/hcloud/schema/server.go
+++ b/hcloud/schema/server.go
@@ -214,3 +214,25 @@ type ServerActionDetachISORequest struct{}
 type ServerActionDetachISOResponse struct {
 	Action Action `json:"action"`
 }
+
+// ServerActionEnableBackupRequest defines the schema for the request to
+// enable backup for a server.
+type ServerActionEnableBackupRequest struct {
+	BackupWindow *string `json:"backup_window,omitempty"`
+}
+
+// ServerActionEnableBackupResponse defines the schema of the response when
+// creating a enable_backup server action.
+type ServerActionEnableBackupResponse struct {
+	Action Action `json:"action"`
+}
+
+// ServerActionDisableBackupRequest defines the schema for the request to
+// disable backup for a server.
+type ServerActionDisableBackupRequest struct{}
+
+// ServerActionDisableBackupResponse defines the schema of the response when
+// creating a disable_backup server action.
+type ServerActionDisableBackupResponse struct {
+	Action Action `json:"action"`
+}
diff --git a/hcloud/server.go b/hcloud/server.go
index 0dd6650..d3733ed 100644
--- a/hcloud/server.go
+++ b/hcloud/server.go
@@ -585,3 +585,46 @@ func (c *ServerClient) DetachISO(ctx context.Context, server *Server) (*Action,
 	}
 	return ActionFromSchema(respBody.Action), resp, nil
 }
+
+// EnableBackup enables backup for a server. Pass in an empty backup window to let the
+// API pick a window for you. See the API documentation at docs.hetzner.cloud for a list
+// of valid backup windows.
+func (c *ServerClient) EnableBackup(ctx context.Context, server *Server, window string) (*Action, *Response, error) {
+	reqBody := schema.ServerActionEnableBackupRequest{}
+	if window != "" {
+		reqBody.BackupWindow = String(window)
+	}
+	reqBodyData, err := json.Marshal(reqBody)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	path := fmt.Sprintf("/servers/%d/actions/enable_backup", server.ID)
+	req, err := c.client.NewRequest(ctx, "POST", path, bytes.NewReader(reqBodyData))
+	if err != nil {
+		return nil, nil, err
+	}
+
+	respBody := schema.ServerActionEnableBackupResponse{}
+	resp, err := c.client.Do(req, &respBody)
+	if err != nil {
+		return nil, resp, err
+	}
+	return ActionFromSchema(respBody.Action), resp, nil
+}
+
+// DisableBackup disables backup for a server.
+func (c *ServerClient) DisableBackup(ctx context.Context, server *Server) (*Action, *Response, error) {
+	path := fmt.Sprintf("/servers/%d/actions/disable_backup", server.ID)
+	req, err := c.client.NewRequest(ctx, "POST", path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	respBody := schema.ServerActionDisableBackupResponse{}
+	resp, err := c.client.Do(req, &respBody)
+	if err != nil {
+		return nil, resp, err
+	}
+	return ActionFromSchema(respBody.Action), resp, nil
+}


Test patch:
diff --git a/hcloud/server_test.go b/hcloud/server_test.go
index 84fd37c..31a5bd8 100644
--- a/hcloud/server_test.go
+++ b/hcloud/server_test.go
@@ -877,3 +877,92 @@ func TestServerClientDetachISO(t *testing.T) {
 		t.Errorf("unexpected action ID: %d", action.ID)
 	}
 }
+
+func TestServerClientEnableBackup(t *testing.T) {
+	var (
+		ctx    = context.Background()
+		server = &Server{ID: 1}
+	)
+
+	t.Run("with a backup window", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		env.Mux.HandleFunc("/servers/1/actions/enable_backup", func(w http.ResponseWriter, r *http.Request) {
+			var reqBody schema.ServerActionEnableBackupRequest
+			if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
+				t.Fatal(err)
+			}
+			if reqBody.BackupWindow == nil || *reqBody.BackupWindow != "9-17" {
+				t.Errorf("unexpected backup window: %v", reqBody.BackupWindow)
+			}
+			json.NewEncoder(w).Encode(schema.ServerActionEnableBackupResponse{
+				Action: schema.Action{
+					ID: 1,
+				},
+			})
+		})
+
+		action, _, err := env.Client.Server.EnableBackup(ctx, server, "9-17")
+		if err != nil {
+			t.Fatal(err)
+		}
+		if action.ID != 1 {
+			t.Errorf("unexpected action ID: %d", action.ID)
+		}
+	})
+
+	t.Run("without a backup window", func(t *testing.T) {
+		env := newTestEnv()
+		defer env.Teardown()
+
+		env.Mux.HandleFunc("/servers/1/actions/enable_backup", func(w http.ResponseWriter, r *http.Request) {
+			var reqBody schema.ServerActionEnableBackupRequest
+			if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil {
+				t.Fatal(err)
+			}
+			if reqBody.BackupWindow != nil {
+				t.Errorf("unexpected backup window: %v", reqBody.BackupWindow)
+			}
+			json.NewEncoder(w).Encode(schema.ServerActionEnableBackupResponse{
+				Action: schema.Action{
+					ID: 1,
+				},
+			})
+		})
+
+		action, _, err := env.Client.Server.EnableBackup(ctx, server, "")
+		if err != nil {
+			t.Fatal(err)
+		}
+		if action.ID != 1 {
+			t.Errorf("unexpected action ID: %d", action.ID)
+		}
+	})
+}
+
+func TestServerClientDisableBackup(t *testing.T) {
+	env := newTestEnv()
+	defer env.Teardown()
+
+	var (
+		ctx    = context.Background()
+		server = &Server{ID: 1}
+	)
+
+	env.Mux.HandleFunc("/servers/1/actions/disable_backup", func(w http.ResponseWriter, r *http.Request) {
+		json.NewEncoder(w).Encode(schema.ServerActionDisableBackupResponse{
+			Action: schema.Action{
+				ID: 1,
+			},
+		})
+	})
+
+	action, _, err := env.Client.Server.DisableBackup(ctx, server)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if action.ID != 1 {
+		t.Errorf("unexpected action ID: %d", action.ID)
+	}
+}