Skip to content

Commit 75da0de

Browse files
authored
Merge pull request #27 from serverscom/add-labels
add labels support; add missing methods where labels used
2 parents 6350167 + 45e72a0 commit 75da0de

File tree

78 files changed

+3024
-253
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+3024
-253
lines changed

pkg/cloud_block_storage_backups.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package serverscom
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
)
7+
8+
const (
9+
cloudBlockStorageBackupPath = "/cloud_block_storage/backups"
10+
cloudBlockStorageBackupPathWithID = cloudBlockStorageBackupPath + "/%s"
11+
actionRestore = "/restore"
12+
)
13+
14+
// CloudBlockStorageBackupsService is an interface for interfacing with Cloud Backup endpoints
15+
// API documentation: https://developers.servers.com/api-documentation/v1/#tag/Cloud-Backup
16+
type CloudBlockStorageBackupsService interface {
17+
// Primary collection
18+
Collection() Collection[CloudBlockStorageBackup]
19+
20+
// Generic operations
21+
Get(ctx context.Context, id string) (*CloudBlockStorageBackup, error)
22+
Create(ctx context.Context, input CloudBlockStorageBackupCreateInput) (*CloudBlockStorageBackup, error)
23+
Update(ctx context.Context, id string, input CloudBlockStorageBackupUpdateInput) (*CloudBlockStorageBackup, error)
24+
Delete(ctx context.Context, id string) (*CloudBlockStorageBackup, error)
25+
Restore(ctx context.Context, id string, input CloudBlockStorageBackupRestoreInput) (*CloudBlockStorageBackup, error)
26+
}
27+
28+
// CloudBlockStorageBackupsHandler handles operations around cloud backups
29+
type CloudBlockStorageBackupsHandler struct {
30+
client *Client
31+
}
32+
33+
// Collection builds a new Collection[CloudBlockStorageBackup] interface
34+
func (h *CloudBlockStorageBackupsHandler) Collection() Collection[CloudBlockStorageBackup] {
35+
return NewCollection[CloudBlockStorageBackup](h.client, cloudBlockStorageBackupPath)
36+
}
37+
38+
// Get a volume backup
39+
// Endpoint: https://developers.servers.com/api-documentation/v1/#tag/Cloud-Backup/operation/GetAVolumeBackup
40+
func (h *CloudBlockStorageBackupsHandler) Get(ctx context.Context, id string) (*CloudBlockStorageBackup, error) {
41+
url := h.client.buildURL(cloudBlockStorageBackupPathWithID, id)
42+
43+
body, err := h.client.buildAndExecRequest(ctx, "GET", url, nil)
44+
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
var backup CloudBlockStorageBackup
50+
if err := json.Unmarshal(body, &backup); err != nil {
51+
return nil, err
52+
}
53+
54+
return &backup, nil
55+
}
56+
57+
// Create a backup from a cloud volume
58+
// Endpoint: https://developers.servers.com/api-documentation/v1/#tag/Cloud-Backup/operation/CreateABackupFromACloudVolume
59+
func (h *CloudBlockStorageBackupsHandler) Create(ctx context.Context, input CloudBlockStorageBackupCreateInput) (*CloudBlockStorageBackup, error) {
60+
payload, err := json.Marshal(input)
61+
62+
if err != nil {
63+
return nil, err
64+
}
65+
66+
url := h.client.buildURL(cloudBlockStorageBackupPath)
67+
68+
body, err := h.client.buildAndExecRequest(ctx, "POST", url, payload)
69+
70+
if err != nil {
71+
return nil, err
72+
}
73+
74+
var backup CloudBlockStorageBackup
75+
if err := json.Unmarshal(body, &backup); err != nil {
76+
return nil, err
77+
}
78+
79+
return &backup, nil
80+
}
81+
82+
// Update a volume backup
83+
// Endpoint: https://developers.servers.com/api-documentation/v1/#tag/Cloud-Backup/operation/UpdateAVolumeBackup
84+
func (h *CloudBlockStorageBackupsHandler) Update(ctx context.Context, id string, input CloudBlockStorageBackupUpdateInput) (*CloudBlockStorageBackup, error) {
85+
payload, err := json.Marshal(input)
86+
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
url := h.client.buildURL(cloudBlockStorageBackupPathWithID, id)
92+
93+
body, err := h.client.buildAndExecRequest(ctx, "PUT", url, payload)
94+
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
var backup CloudBlockStorageBackup
100+
if err := json.Unmarshal(body, &backup); err != nil {
101+
return nil, err
102+
}
103+
104+
return &backup, nil
105+
}
106+
107+
// Delete a volume backup
108+
// Endpoint: https://developers.servers.com/api-documentation/v1/#tag/Cloud-Backup/operation/DeleteAVolumeBackup
109+
func (h *CloudBlockStorageBackupsHandler) Delete(ctx context.Context, id string) (*CloudBlockStorageBackup, error) {
110+
url := h.client.buildURL(cloudBlockStorageBackupPathWithID, id)
111+
112+
body, err := h.client.buildAndExecRequest(ctx, "DELETE", url, nil)
113+
114+
if err != nil {
115+
return nil, err
116+
}
117+
118+
var backup CloudBlockStorageBackup
119+
if err := json.Unmarshal(body, &backup); err != nil {
120+
return nil, err
121+
}
122+
123+
return &backup, nil
124+
}
125+
126+
// Restore a volume backup
127+
// Endpoint: https://developers.servers.com/api-documentation/v1/#tag/Cloud-Backup/operation/RestoreAVolumeBackup
128+
func (h *CloudBlockStorageBackupsHandler) Restore(ctx context.Context, id string, input CloudBlockStorageBackupRestoreInput) (*CloudBlockStorageBackup, error) {
129+
payload, err := json.Marshal(input)
130+
131+
if err != nil {
132+
return nil, err
133+
}
134+
135+
url := h.client.buildURL(cloudBlockStorageBackupPathWithID+actionRestore, id)
136+
137+
body, err := h.client.buildAndExecRequest(ctx, "POST", url, payload)
138+
139+
if err != nil {
140+
return nil, err
141+
}
142+
143+
var backup CloudBlockStorageBackup
144+
if err := json.Unmarshal(body, &backup); err != nil {
145+
return nil, err
146+
}
147+
148+
return &backup, nil
149+
}
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
package serverscom
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
const (
11+
backupVolumeID = "X7ax9byv"
12+
)
13+
14+
func TestCloudBlockStorageBackupsCollection(t *testing.T) {
15+
g := NewGomegaWithT(t)
16+
17+
ts, client := newFakeServer().
18+
WithRequestPath("/cloud_block_storage/backups").
19+
WithRequestMethod("GET").
20+
WithResponseBodyStubInline(`[]`).
21+
WithResponseCode(200).
22+
Build()
23+
24+
defer ts.Close()
25+
26+
collection := client.CloudBlockStorageBackups.Collection()
27+
28+
ctx := context.TODO()
29+
30+
list, err := collection.List(ctx)
31+
32+
g.Expect(err).To(BeNil())
33+
g.Expect(list).To(BeEmpty())
34+
g.Expect(collection.HasNextPage()).To(Equal(false))
35+
g.Expect(collection.HasPreviousPage()).To(Equal(false))
36+
g.Expect(collection.HasFirstPage()).To(Equal(false))
37+
g.Expect(collection.HasLastPage()).To(Equal(false))
38+
}
39+
40+
func TestCloudBlockStorageBackupsCreate(t *testing.T) {
41+
g := NewGomegaWithT(t)
42+
43+
ts, client := newFakeServer().
44+
WithRequestPath("/cloud_block_storage/backups").
45+
WithRequestMethod("POST").
46+
WithResponseBodyStubFile("fixtures/cloud_backups/create_response.json").
47+
WithResponseCode(201).
48+
Build()
49+
50+
defer ts.Close()
51+
52+
input := CloudBlockStorageBackupCreateInput{
53+
VolumeID: backupVolumeID,
54+
Name: "test-backup-1",
55+
Incremental: true,
56+
Force: false,
57+
Labels: map[string]string{"env": "test"},
58+
}
59+
60+
ctx := context.TODO()
61+
62+
cloudBackup, err := client.CloudBlockStorageBackups.Create(ctx, input)
63+
64+
g.Expect(err).To(BeNil())
65+
g.Expect(cloudBackup).ToNot(BeNil())
66+
67+
g.Expect(cloudBackup.ID).To(Equal(backupVolumeID))
68+
g.Expect(cloudBackup.Status).To(Equal("pending"))
69+
g.Expect(cloudBackup.Name).To(Equal("test-backup-1"))
70+
g.Expect(cloudBackup.OpenstackVolumeUUID).To(Equal("1ac953dc-2414-4831-8124-c2dcbd405926"))
71+
g.Expect(cloudBackup.OpenstackUUID).To(BeNil())
72+
g.Expect(cloudBackup.Labels).To(Equal(map[string]string{"env": "test"}))
73+
g.Expect(cloudBackup.Created.String()).To(Equal("2024-11-11 09:57:28 +0000 UTC"))
74+
}
75+
76+
func TestCloudBlockStorageBackupsGet(t *testing.T) {
77+
g := NewGomegaWithT(t)
78+
79+
ts, client := newFakeServer().
80+
WithRequestPath("/cloud_block_storage/backups/" + backupVolumeID).
81+
WithRequestMethod("GET").
82+
WithResponseBodyStubFile("fixtures/cloud_backups/get_response.json").
83+
WithResponseCode(201).
84+
Build()
85+
86+
defer ts.Close()
87+
88+
ctx := context.TODO()
89+
90+
cloudBackup, err := client.CloudBlockStorageBackups.Get(ctx, backupVolumeID)
91+
92+
g.Expect(err).To(BeNil())
93+
g.Expect(cloudBackup).ToNot(BeNil())
94+
95+
g.Expect(cloudBackup.ID).To(Equal(backupVolumeID))
96+
g.Expect(cloudBackup.Status).To(Equal("available"))
97+
g.Expect(cloudBackup.Name).To(Equal("test-backup-1"))
98+
g.Expect(cloudBackup.Size).To(Equal(1))
99+
g.Expect(cloudBackup.RegionID).To(Equal(1))
100+
g.Expect(*cloudBackup.OpenstackUUID).To(Equal("d1473317-bb2c-4d68-992c-0c117a68c01a"))
101+
g.Expect(cloudBackup.OpenstackVolumeUUID).To(Equal("1ac953dc-2414-4831-8124-c2dcbd405926"))
102+
g.Expect(cloudBackup.Labels).To(Equal(map[string]string{"env": "test"}))
103+
g.Expect(cloudBackup.Created.String()).To(Equal("2024-11-11 09:57:28 +0000 UTC"))
104+
}
105+
106+
func TestCloudBlockStorageBackupsUpdate(t *testing.T) {
107+
g := NewGomegaWithT(t)
108+
109+
ts, client := newFakeServer().
110+
WithRequestPath("/cloud_block_storage/backups/" + backupVolumeID).
111+
WithRequestMethod("PUT").
112+
WithResponseBodyStubFile("fixtures/cloud_backups/update_response.json").
113+
WithResponseCode(202).
114+
Build()
115+
116+
defer ts.Close()
117+
118+
ctx := context.TODO()
119+
120+
newLabels := map[string]string{"env": "new-test"}
121+
122+
cloudBackup, err := client.CloudBlockStorageBackups.Update(ctx, backupVolumeID, CloudBlockStorageBackupUpdateInput{Labels: newLabels})
123+
124+
g.Expect(err).To(BeNil())
125+
g.Expect(cloudBackup).ToNot(BeNil())
126+
127+
g.Expect(cloudBackup.ID).To(Equal(backupVolumeID))
128+
g.Expect(cloudBackup.Status).To(Equal("available"))
129+
g.Expect(cloudBackup.Name).To(Equal("test-backup-1"))
130+
g.Expect(cloudBackup.Size).To(Equal(1))
131+
g.Expect(cloudBackup.RegionID).To(Equal(1))
132+
g.Expect(*cloudBackup.OpenstackUUID).To(Equal("d1473317-bb2c-4d68-992c-0c117a68c01a"))
133+
g.Expect(cloudBackup.OpenstackVolumeUUID).To(Equal("1ac953dc-2414-4831-8124-c2dcbd405926"))
134+
g.Expect(cloudBackup.Labels).To(Equal(newLabels))
135+
g.Expect(cloudBackup.Created.String()).To(Equal("2024-11-11 09:57:28 +0000 UTC"))
136+
}
137+
138+
func TestCloudBlockStorageBackupsDelete(t *testing.T) {
139+
g := NewGomegaWithT(t)
140+
141+
ts, client := newFakeServer().
142+
WithRequestPath("/cloud_block_storage/backups/" + backupVolumeID).
143+
WithRequestMethod("DELETE").
144+
WithResponseBodyStubFile("fixtures/cloud_backups/delete_response.json").
145+
WithResponseCode(202).
146+
Build()
147+
148+
defer ts.Close()
149+
150+
ctx := context.TODO()
151+
152+
cloudBackup, err := client.CloudBlockStorageBackups.Delete(ctx, backupVolumeID)
153+
154+
g.Expect(err).To(BeNil())
155+
g.Expect(cloudBackup).ToNot(BeNil())
156+
157+
g.Expect(cloudBackup.ID).To(Equal(backupVolumeID))
158+
g.Expect(cloudBackup.Status).To(Equal("deleting"))
159+
g.Expect(cloudBackup.Name).To(Equal("test-backup-1"))
160+
g.Expect(cloudBackup.Size).To(Equal(1))
161+
g.Expect(cloudBackup.RegionID).To(Equal(1))
162+
g.Expect(*cloudBackup.OpenstackUUID).To(Equal("d1473317-bb2c-4d68-992c-0c117a68c01a"))
163+
g.Expect(cloudBackup.OpenstackVolumeUUID).To(Equal("1ac953dc-2414-4831-8124-c2dcbd405926"))
164+
g.Expect(cloudBackup.Labels).To(Equal(map[string]string{"env": "test"}))
165+
g.Expect(cloudBackup.Created.String()).To(Equal("2024-11-11 09:57:28 +0000 UTC"))
166+
}
167+
168+
func TestCloudBlockStorageBackupsRestore(t *testing.T) {
169+
g := NewGomegaWithT(t)
170+
171+
ts, client := newFakeServer().
172+
WithRequestPath("/cloud_block_storage/backups/" + backupVolumeID + "/restore").
173+
WithRequestMethod("POST").
174+
WithResponseBodyStubFile("fixtures/cloud_backups/restore_response.json").
175+
WithResponseCode(202).
176+
Build()
177+
178+
defer ts.Close()
179+
180+
ctx := context.TODO()
181+
182+
newVolumeID := backupVolumeID
183+
184+
cloudBackup, err := client.CloudBlockStorageBackups.Restore(ctx, backupVolumeID, CloudBlockStorageBackupRestoreInput{VolumeID: newVolumeID})
185+
186+
g.Expect(err).To(BeNil())
187+
g.Expect(cloudBackup).ToNot(BeNil())
188+
189+
g.Expect(cloudBackup.ID).To(Equal(backupVolumeID))
190+
g.Expect(cloudBackup.Status).To(Equal("restoring"))
191+
g.Expect(cloudBackup.Name).To(Equal("test-backup-1"))
192+
g.Expect(cloudBackup.Size).To(Equal(1))
193+
g.Expect(cloudBackup.RegionID).To(Equal(1))
194+
g.Expect(*cloudBackup.OpenstackUUID).To(Equal("d1473317-bb2c-4d68-992c-0c117a68c01a"))
195+
g.Expect(cloudBackup.OpenstackVolumeUUID).To(Equal("1ac953dc-2414-4831-8124-c2dcbd405926"))
196+
g.Expect(cloudBackup.Labels).To(Equal(map[string]string{"env": "test"}))
197+
g.Expect(cloudBackup.Created.String()).To(Equal("2024-11-11 09:57:28 +0000 UTC"))
198+
}

0 commit comments

Comments
 (0)