Skip to content

Commit 80081fe

Browse files
Merge pull request #226 from civo/feat/vol-snapshots
Feat/vol snapshots
2 parents 01d984c + 2e3e5c6 commit 80081fe

File tree

4 files changed

+358
-0
lines changed

4 files changed

+358
-0
lines changed

volume.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type VolumeConfig struct {
4141
SizeGigabytes int `json:"size_gb"`
4242
Bootable bool `json:"bootable"`
4343
VolumeType string `json:"volume_type"`
44+
SnapshotID string `json:"snapshot_id,omitempty"`
4445
}
4546

4647
// VolumeAttachConfig is the configuration used to attach volume
@@ -242,3 +243,56 @@ func (c *Client) DeleteVolume(id string) (*SimpleResponse, error) {
242243

243244
return c.DecodeSimpleResponse(resp)
244245
}
246+
247+
// GetVolumeSnapshotByVolumeID retrieves a specific volume snapshot by volume ID and snapshot ID
248+
func (c *Client) GetVolumeSnapshotByVolumeID(volumeID, snapshotID string) (VolumeSnapshot, error) {
249+
resp, err := c.SendGetRequest(fmt.Sprintf("/v2/volumes/%s/snapshots/%s", volumeID, snapshotID))
250+
if err != nil {
251+
return VolumeSnapshot{}, decodeError(err)
252+
}
253+
var volumeSnapshot = VolumeSnapshot{}
254+
if err := json.NewDecoder(bytes.NewReader(resp)).Decode(&volumeSnapshot); err != nil {
255+
return VolumeSnapshot{}, err
256+
}
257+
return volumeSnapshot, nil
258+
}
259+
260+
// ListVolumeSnapshotsByVolumeID returns all snapshots for a specific volume by volume ID
261+
func (c *Client) ListVolumeSnapshotsByVolumeID(volumeID string) ([]VolumeSnapshot, error) {
262+
resp, err := c.SendGetRequest(fmt.Sprintf("/v2/volumes/%s/snapshots", volumeID))
263+
if err != nil {
264+
return nil, decodeError(err)
265+
}
266+
267+
var volumeSnapshots = make([]VolumeSnapshot, 0)
268+
if err := json.NewDecoder(bytes.NewReader(resp)).Decode(&volumeSnapshots); err != nil {
269+
return nil, err
270+
}
271+
272+
return volumeSnapshots, nil
273+
}
274+
275+
// CreateVolumeSnapshot creates a snapshot of a volume
276+
func (c *Client) CreateVolumeSnapshot(volumeID string, config *VolumeSnapshotConfig) (*VolumeSnapshot, error) {
277+
body, err := c.SendPostRequest(fmt.Sprintf("/v2/volumes/%s/snapshots", volumeID), config)
278+
if err != nil {
279+
return nil, decodeError(err)
280+
}
281+
282+
var result = &VolumeSnapshot{}
283+
if err := json.NewDecoder(bytes.NewReader(body)).Decode(result); err != nil {
284+
return nil, err
285+
}
286+
287+
return result, nil
288+
}
289+
290+
// DeleteVolumeAndAllSnapshot deletes a volume and all its snapshots
291+
func (c *Client) DeleteVolumeAndAllSnapshot(volumeID string) (*SimpleResponse, error) {
292+
resp, err := c.SendDeleteRequest(fmt.Sprintf("/v2/volumes/%s?delete_snapshot=true", volumeID))
293+
if err != nil {
294+
return nil, decodeError(err)
295+
}
296+
297+
return c.DecodeSimpleResponse(resp)
298+
}

volume_snapshot.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package civogo
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
)
8+
9+
// VolumeSnapshot is the point-in-time copy of a Volume
10+
type VolumeSnapshot struct {
11+
Name string `json:"name"`
12+
SnapshotID string `json:"snapshot_id"`
13+
SnapshotDescription string `json:"snapshot_description"`
14+
VolumeID string `json:"volume_id"`
15+
InstanceID string `json:"instance_id,omitempty"`
16+
SourceVolumeName string `json:"source_volume_name"`
17+
RestoreSize int `json:"restore_size"`
18+
State string `json:"state"`
19+
CreationTime string `json:"creation_time,omitempty"`
20+
}
21+
22+
// VolumeSnapshotConfig is the configuration for creating a new VolumeSnapshot
23+
type VolumeSnapshotConfig struct {
24+
Name string `json:"name"`
25+
Description string `json:"description"`
26+
Region string `json:"region"`
27+
}
28+
29+
// ListVolumeSnapshots returns all snapshots owned by the calling API account
30+
func (c *Client) ListVolumeSnapshots() ([]VolumeSnapshot, error) {
31+
resp, err := c.SendGetRequest("/v2/snapshots?resource_type=volume")
32+
if err != nil {
33+
return nil, decodeError(err)
34+
}
35+
36+
var volumeSnapshots = make([]VolumeSnapshot, 0)
37+
if err := json.NewDecoder(bytes.NewReader(resp)).Decode(&volumeSnapshots); err != nil {
38+
return nil, err
39+
}
40+
41+
return volumeSnapshots, nil
42+
}
43+
44+
// GetVolumeSnapshot finds a volume by the full ID
45+
func (c *Client) GetVolumeSnapshot(id string) (VolumeSnapshot, error) {
46+
resp, err := c.SendGetRequest(fmt.Sprintf("/v2/snapshots/%s?resource_type=volume", id))
47+
if err != nil {
48+
return VolumeSnapshot{}, decodeError(err)
49+
}
50+
var volumeSnapshot = VolumeSnapshot{}
51+
if err := json.NewDecoder(bytes.NewReader(resp)).Decode(&volumeSnapshot); err != nil {
52+
return VolumeSnapshot{}, err
53+
}
54+
return volumeSnapshot, nil
55+
}
56+
57+
// DeleteVolumeSnapshot deletes a volume snapshot
58+
func (c *Client) DeleteVolumeSnapshot(id string) (*SimpleResponse, error) {
59+
resp, err := c.SendDeleteRequest(fmt.Sprintf("/v2/snapshots/%s", id))
60+
if err != nil {
61+
return nil, decodeError(err)
62+
}
63+
64+
return c.DecodeSimpleResponse(resp)
65+
}

volume_snapshot_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package civogo
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestListVolumeSnapshots(t *testing.T) {
9+
client, server, _ := NewClientForTesting(map[string]string{
10+
"/v2/snapshots?region=TEST&resource_type=volume": `[{
11+
"name": "test-snapshot",
12+
"snapshot_id": "12345",
13+
"snapshot_description": "snapshot for test",
14+
"volume_id": "12345",
15+
"source_volume_name": "test-volume",
16+
"instance_id": "ins1234",
17+
"restore_size": 20,
18+
"state": "available",
19+
"creation_time": "2020-01-01T00:00:00Z"
20+
}]`,
21+
})
22+
defer server.Close()
23+
24+
got, err := client.ListVolumeSnapshots()
25+
26+
if err != nil {
27+
t.Errorf("Request returned an error: %s", err)
28+
return
29+
}
30+
31+
expected := []VolumeSnapshot{
32+
{
33+
Name: "test-snapshot",
34+
SnapshotID: "12345",
35+
SnapshotDescription: "snapshot for test",
36+
VolumeID: "12345",
37+
SourceVolumeName: "test-volume",
38+
InstanceID: "ins1234",
39+
RestoreSize: 20,
40+
State: "available",
41+
CreationTime: "2020-01-01T00:00:00Z",
42+
},
43+
}
44+
45+
if !reflect.DeepEqual(got, expected) {
46+
t.Errorf("Expected %+v, got %+v", expected, got)
47+
}
48+
}
49+
50+
func TestGetVolumeSnapshot(t *testing.T) {
51+
client, server, _ := NewClientForTesting(map[string]string{
52+
"/v2/snapshots/snapshot-uuid?region=TEST&resource_type=volume": `{
53+
"name": "test-snapshot",
54+
"snapshot_id": "snapshot-uuid",
55+
"snapshot_description": "snapshot for testing",
56+
"volume_id": "12345",
57+
"source_volume_name": "test-volume",
58+
"instance_id": "ins1234",
59+
"restore_size": 20,
60+
"state": "available",
61+
"creation_time": "2020-01-01T00:00:00Z"
62+
}`,
63+
})
64+
defer server.Close()
65+
got, err := client.GetVolumeSnapshot("snapshot-uuid")
66+
67+
if err != nil {
68+
t.Errorf("Request returned an error: %s", err)
69+
return
70+
}
71+
72+
expected := VolumeSnapshot{
73+
Name: "test-snapshot",
74+
SnapshotID: "snapshot-uuid",
75+
SnapshotDescription: "snapshot for testing",
76+
VolumeID: "12345",
77+
SourceVolumeName: "test-volume",
78+
InstanceID: "ins1234",
79+
RestoreSize: 20,
80+
State: "available",
81+
CreationTime: "2020-01-01T00:00:00Z",
82+
}
83+
84+
if !reflect.DeepEqual(got, expected) {
85+
t.Errorf("Expected %+v, got %+v", expected, got)
86+
}
87+
}
88+
89+
func TestDeleteVolumeSnapshot(t *testing.T) {
90+
client, server, _ := NewClientForTesting(map[string]string{
91+
"/v2/snapshots/12346": `{"result": "success"}`,
92+
})
93+
defer server.Close()
94+
got, err := client.DeleteVolumeSnapshot("12346")
95+
96+
if err != nil {
97+
t.Errorf("Request returned an error: %s", err)
98+
return
99+
}
100+
101+
expected := &SimpleResponse{Result: "success"}
102+
103+
if !reflect.DeepEqual(got, expected) {
104+
t.Errorf("Expected %+v, got %+v", expected, got)
105+
}
106+
}

volume_test.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,136 @@ func TestResizeVolume(t *testing.T) {
260260
t.Errorf("Expected %+v, got %+v", expected, got)
261261
}
262262
}
263+
264+
func TestCreateVolumeSnapshot(t *testing.T) {
265+
client, server, _ := NewClientForTesting(map[string]string{
266+
"/v2/volumes/12346/snapshot": `{
267+
"snapshot_id": "12345",
268+
"name": "test-snapshot",
269+
"snapshot_description": "snapshot for testing",
270+
"volume_id": "12346",
271+
"instance_id": "instance-123",
272+
"source_volume_name": "source-volume",
273+
"state": "available",
274+
"creation_time": "2020-01-01T00:00:00Z"
275+
}`,
276+
})
277+
defer server.Close()
278+
cfg := &VolumeSnapshotConfig{Name: "my-snapshot"}
279+
got, err := client.CreateVolumeSnapshot("12346", cfg)
280+
if err != nil {
281+
t.Errorf("Request returned an error: %s", err)
282+
return
283+
}
284+
285+
expected := &VolumeSnapshot{
286+
SnapshotID: "12345",
287+
Name: "test-snapshot",
288+
SnapshotDescription: "snapshot for testing",
289+
VolumeID: "12346",
290+
InstanceID: "instance-123",
291+
SourceVolumeName: "source-volume",
292+
State: "available",
293+
CreationTime: "2020-01-01T00:00:00Z",
294+
}
295+
if !reflect.DeepEqual(got, expected) {
296+
t.Errorf("Expected %+v, got %+v", expected, got)
297+
}
298+
}
299+
300+
func TestGetVolumeSnapshotByVolumeID(t *testing.T) {
301+
client, server, _ := NewClientForTesting(map[string]string{
302+
"/v2/volumes/12346/snapshots/12345": `{
303+
"snapshot_id": "12345",
304+
"name": "test-snapshot",
305+
"snapshot_description": "snapshot for testing",
306+
"volume_id": "12346",
307+
"instance_id": "instance-123",
308+
"source_volume_name": "source-volume",
309+
"state": "available",
310+
"creation_time": "2020-01-01T00:00:00Z"
311+
}`,
312+
})
313+
defer server.Close()
314+
315+
got, err := client.GetVolumeSnapshotByVolumeID("12346", "12345")
316+
if err != nil {
317+
t.Errorf("Request returned an error: %s", err)
318+
return
319+
}
320+
321+
expected := VolumeSnapshot{
322+
SnapshotID: "12345",
323+
Name: "test-snapshot",
324+
SnapshotDescription: "snapshot for testing",
325+
VolumeID: "12346",
326+
InstanceID: "instance-123",
327+
SourceVolumeName: "source-volume",
328+
State: "available",
329+
CreationTime: "2020-01-01T00:00:00Z",
330+
}
331+
332+
if !reflect.DeepEqual(got, expected) {
333+
t.Errorf("Expected %+v, got %+v", expected, got)
334+
}
335+
}
336+
337+
func TestListVolumeSnapshotsByVolumeID(t *testing.T) {
338+
client, server, _ := NewClientForTesting(map[string]string{
339+
"/v2/volumes/12346/snapshots": `[{
340+
"snapshot_id": "12345",
341+
"name": "test-snapshot",
342+
"snapshot_description": "snapshot for testing",
343+
"volume_id": "12346",
344+
"instance_id": "instance-123",
345+
"source_volume_name": "source-volume",
346+
"state": "available",
347+
"creation_time": "2020-01-01T00:00:00Z"
348+
}]`,
349+
})
350+
defer server.Close()
351+
352+
got, err := client.ListVolumeSnapshotsByVolumeID("12346")
353+
if err != nil {
354+
t.Errorf("Request returned an error: %s", err)
355+
return
356+
}
357+
358+
expected := []VolumeSnapshot{
359+
{
360+
SnapshotID: "12345",
361+
Name: "test-snapshot",
362+
SnapshotDescription: "snapshot for testing",
363+
VolumeID: "12346",
364+
InstanceID: "instance-123",
365+
SourceVolumeName: "source-volume",
366+
State: "available",
367+
CreationTime: "2020-01-01T00:00:00Z",
368+
},
369+
}
370+
371+
if !reflect.DeepEqual(got, expected) {
372+
t.Errorf("Expected %+v, got %+v", expected, got)
373+
}
374+
}
375+
376+
func TestDeleteVolumeAndAllSnapshot(t *testing.T) {
377+
client, server, _ := NewClientForTesting(map[string]string{
378+
"/v2/volumes/12346?delete_snapshot=true": `{"result": "success"}`,
379+
})
380+
defer server.Close()
381+
382+
got, err := client.DeleteVolumeAndAllSnapshot("12346")
383+
if err != nil {
384+
t.Errorf("Request returned an error: %s", err)
385+
return
386+
}
387+
388+
expected := &SimpleResponse{
389+
Result: "success",
390+
}
391+
392+
if !reflect.DeepEqual(got, expected) {
393+
t.Errorf("Expected %+v, got %+v", expected, got)
394+
}
395+
}

0 commit comments

Comments
 (0)