Skip to content

Commit

Permalink
Merge pull request #226 from civo/feat/vol-snapshots
Browse files Browse the repository at this point in the history
Feat/vol snapshots
  • Loading branch information
TheRealSibasishBehera authored Jan 2, 2025
2 parents 01d984c + 2e3e5c6 commit 80081fe
Show file tree
Hide file tree
Showing 4 changed files with 358 additions and 0 deletions.
54 changes: 54 additions & 0 deletions volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type VolumeConfig struct {
SizeGigabytes int `json:"size_gb"`
Bootable bool `json:"bootable"`
VolumeType string `json:"volume_type"`
SnapshotID string `json:"snapshot_id,omitempty"`
}

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

return c.DecodeSimpleResponse(resp)
}

// GetVolumeSnapshotByVolumeID retrieves a specific volume snapshot by volume ID and snapshot ID
func (c *Client) GetVolumeSnapshotByVolumeID(volumeID, snapshotID string) (VolumeSnapshot, error) {
resp, err := c.SendGetRequest(fmt.Sprintf("/v2/volumes/%s/snapshots/%s", volumeID, snapshotID))
if err != nil {
return VolumeSnapshot{}, decodeError(err)
}
var volumeSnapshot = VolumeSnapshot{}
if err := json.NewDecoder(bytes.NewReader(resp)).Decode(&volumeSnapshot); err != nil {
return VolumeSnapshot{}, err
}
return volumeSnapshot, nil
}

// ListVolumeSnapshotsByVolumeID returns all snapshots for a specific volume by volume ID
func (c *Client) ListVolumeSnapshotsByVolumeID(volumeID string) ([]VolumeSnapshot, error) {
resp, err := c.SendGetRequest(fmt.Sprintf("/v2/volumes/%s/snapshots", volumeID))
if err != nil {
return nil, decodeError(err)
}

var volumeSnapshots = make([]VolumeSnapshot, 0)
if err := json.NewDecoder(bytes.NewReader(resp)).Decode(&volumeSnapshots); err != nil {
return nil, err
}

return volumeSnapshots, nil
}

// CreateVolumeSnapshot creates a snapshot of a volume
func (c *Client) CreateVolumeSnapshot(volumeID string, config *VolumeSnapshotConfig) (*VolumeSnapshot, error) {
body, err := c.SendPostRequest(fmt.Sprintf("/v2/volumes/%s/snapshots", volumeID), config)
if err != nil {
return nil, decodeError(err)
}

var result = &VolumeSnapshot{}
if err := json.NewDecoder(bytes.NewReader(body)).Decode(result); err != nil {
return nil, err
}

return result, nil
}

// DeleteVolumeAndAllSnapshot deletes a volume and all its snapshots
func (c *Client) DeleteVolumeAndAllSnapshot(volumeID string) (*SimpleResponse, error) {
resp, err := c.SendDeleteRequest(fmt.Sprintf("/v2/volumes/%s?delete_snapshot=true", volumeID))
if err != nil {
return nil, decodeError(err)
}

return c.DecodeSimpleResponse(resp)
}
65 changes: 65 additions & 0 deletions volume_snapshot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package civogo

import (
"bytes"
"encoding/json"
"fmt"
)

// VolumeSnapshot is the point-in-time copy of a Volume
type VolumeSnapshot struct {
Name string `json:"name"`
SnapshotID string `json:"snapshot_id"`
SnapshotDescription string `json:"snapshot_description"`
VolumeID string `json:"volume_id"`
InstanceID string `json:"instance_id,omitempty"`
SourceVolumeName string `json:"source_volume_name"`
RestoreSize int `json:"restore_size"`
State string `json:"state"`
CreationTime string `json:"creation_time,omitempty"`
}

// VolumeSnapshotConfig is the configuration for creating a new VolumeSnapshot
type VolumeSnapshotConfig struct {
Name string `json:"name"`
Description string `json:"description"`
Region string `json:"region"`
}

// ListVolumeSnapshots returns all snapshots owned by the calling API account
func (c *Client) ListVolumeSnapshots() ([]VolumeSnapshot, error) {
resp, err := c.SendGetRequest("/v2/snapshots?resource_type=volume")
if err != nil {
return nil, decodeError(err)
}

var volumeSnapshots = make([]VolumeSnapshot, 0)
if err := json.NewDecoder(bytes.NewReader(resp)).Decode(&volumeSnapshots); err != nil {
return nil, err
}

return volumeSnapshots, nil
}

// GetVolumeSnapshot finds a volume by the full ID
func (c *Client) GetVolumeSnapshot(id string) (VolumeSnapshot, error) {
resp, err := c.SendGetRequest(fmt.Sprintf("/v2/snapshots/%s?resource_type=volume", id))
if err != nil {
return VolumeSnapshot{}, decodeError(err)
}
var volumeSnapshot = VolumeSnapshot{}
if err := json.NewDecoder(bytes.NewReader(resp)).Decode(&volumeSnapshot); err != nil {
return VolumeSnapshot{}, err
}
return volumeSnapshot, nil
}

// DeleteVolumeSnapshot deletes a volume snapshot
func (c *Client) DeleteVolumeSnapshot(id string) (*SimpleResponse, error) {
resp, err := c.SendDeleteRequest(fmt.Sprintf("/v2/snapshots/%s", id))
if err != nil {
return nil, decodeError(err)
}

return c.DecodeSimpleResponse(resp)
}
106 changes: 106 additions & 0 deletions volume_snapshot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package civogo

import (
"reflect"
"testing"
)

func TestListVolumeSnapshots(t *testing.T) {
client, server, _ := NewClientForTesting(map[string]string{
"/v2/snapshots?region=TEST&resource_type=volume": `[{
"name": "test-snapshot",
"snapshot_id": "12345",
"snapshot_description": "snapshot for test",
"volume_id": "12345",
"source_volume_name": "test-volume",
"instance_id": "ins1234",
"restore_size": 20,
"state": "available",
"creation_time": "2020-01-01T00:00:00Z"
}]`,
})
defer server.Close()

got, err := client.ListVolumeSnapshots()

if err != nil {
t.Errorf("Request returned an error: %s", err)
return
}

expected := []VolumeSnapshot{
{
Name: "test-snapshot",
SnapshotID: "12345",
SnapshotDescription: "snapshot for test",
VolumeID: "12345",
SourceVolumeName: "test-volume",
InstanceID: "ins1234",
RestoreSize: 20,
State: "available",
CreationTime: "2020-01-01T00:00:00Z",
},
}

if !reflect.DeepEqual(got, expected) {
t.Errorf("Expected %+v, got %+v", expected, got)
}
}

func TestGetVolumeSnapshot(t *testing.T) {
client, server, _ := NewClientForTesting(map[string]string{
"/v2/snapshots/snapshot-uuid?region=TEST&resource_type=volume": `{
"name": "test-snapshot",
"snapshot_id": "snapshot-uuid",
"snapshot_description": "snapshot for testing",
"volume_id": "12345",
"source_volume_name": "test-volume",
"instance_id": "ins1234",
"restore_size": 20,
"state": "available",
"creation_time": "2020-01-01T00:00:00Z"
}`,
})
defer server.Close()
got, err := client.GetVolumeSnapshot("snapshot-uuid")

if err != nil {
t.Errorf("Request returned an error: %s", err)
return
}

expected := VolumeSnapshot{
Name: "test-snapshot",
SnapshotID: "snapshot-uuid",
SnapshotDescription: "snapshot for testing",
VolumeID: "12345",
SourceVolumeName: "test-volume",
InstanceID: "ins1234",
RestoreSize: 20,
State: "available",
CreationTime: "2020-01-01T00:00:00Z",
}

if !reflect.DeepEqual(got, expected) {
t.Errorf("Expected %+v, got %+v", expected, got)
}
}

func TestDeleteVolumeSnapshot(t *testing.T) {
client, server, _ := NewClientForTesting(map[string]string{
"/v2/snapshots/12346": `{"result": "success"}`,
})
defer server.Close()
got, err := client.DeleteVolumeSnapshot("12346")

if err != nil {
t.Errorf("Request returned an error: %s", err)
return
}

expected := &SimpleResponse{Result: "success"}

if !reflect.DeepEqual(got, expected) {
t.Errorf("Expected %+v, got %+v", expected, got)
}
}
133 changes: 133 additions & 0 deletions volume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,136 @@ func TestResizeVolume(t *testing.T) {
t.Errorf("Expected %+v, got %+v", expected, got)
}
}

func TestCreateVolumeSnapshot(t *testing.T) {
client, server, _ := NewClientForTesting(map[string]string{
"/v2/volumes/12346/snapshot": `{
"snapshot_id": "12345",
"name": "test-snapshot",
"snapshot_description": "snapshot for testing",
"volume_id": "12346",
"instance_id": "instance-123",
"source_volume_name": "source-volume",
"state": "available",
"creation_time": "2020-01-01T00:00:00Z"
}`,
})
defer server.Close()
cfg := &VolumeSnapshotConfig{Name: "my-snapshot"}
got, err := client.CreateVolumeSnapshot("12346", cfg)
if err != nil {
t.Errorf("Request returned an error: %s", err)
return
}

expected := &VolumeSnapshot{
SnapshotID: "12345",
Name: "test-snapshot",
SnapshotDescription: "snapshot for testing",
VolumeID: "12346",
InstanceID: "instance-123",
SourceVolumeName: "source-volume",
State: "available",
CreationTime: "2020-01-01T00:00:00Z",
}
if !reflect.DeepEqual(got, expected) {
t.Errorf("Expected %+v, got %+v", expected, got)
}
}

func TestGetVolumeSnapshotByVolumeID(t *testing.T) {
client, server, _ := NewClientForTesting(map[string]string{
"/v2/volumes/12346/snapshots/12345": `{
"snapshot_id": "12345",
"name": "test-snapshot",
"snapshot_description": "snapshot for testing",
"volume_id": "12346",
"instance_id": "instance-123",
"source_volume_name": "source-volume",
"state": "available",
"creation_time": "2020-01-01T00:00:00Z"
}`,
})
defer server.Close()

got, err := client.GetVolumeSnapshotByVolumeID("12346", "12345")
if err != nil {
t.Errorf("Request returned an error: %s", err)
return
}

expected := VolumeSnapshot{
SnapshotID: "12345",
Name: "test-snapshot",
SnapshotDescription: "snapshot for testing",
VolumeID: "12346",
InstanceID: "instance-123",
SourceVolumeName: "source-volume",
State: "available",
CreationTime: "2020-01-01T00:00:00Z",
}

if !reflect.DeepEqual(got, expected) {
t.Errorf("Expected %+v, got %+v", expected, got)
}
}

func TestListVolumeSnapshotsByVolumeID(t *testing.T) {
client, server, _ := NewClientForTesting(map[string]string{
"/v2/volumes/12346/snapshots": `[{
"snapshot_id": "12345",
"name": "test-snapshot",
"snapshot_description": "snapshot for testing",
"volume_id": "12346",
"instance_id": "instance-123",
"source_volume_name": "source-volume",
"state": "available",
"creation_time": "2020-01-01T00:00:00Z"
}]`,
})
defer server.Close()

got, err := client.ListVolumeSnapshotsByVolumeID("12346")
if err != nil {
t.Errorf("Request returned an error: %s", err)
return
}

expected := []VolumeSnapshot{
{
SnapshotID: "12345",
Name: "test-snapshot",
SnapshotDescription: "snapshot for testing",
VolumeID: "12346",
InstanceID: "instance-123",
SourceVolumeName: "source-volume",
State: "available",
CreationTime: "2020-01-01T00:00:00Z",
},
}

if !reflect.DeepEqual(got, expected) {
t.Errorf("Expected %+v, got %+v", expected, got)
}
}

func TestDeleteVolumeAndAllSnapshot(t *testing.T) {
client, server, _ := NewClientForTesting(map[string]string{
"/v2/volumes/12346?delete_snapshot=true": `{"result": "success"}`,
})
defer server.Close()

got, err := client.DeleteVolumeAndAllSnapshot("12346")
if err != nil {
t.Errorf("Request returned an error: %s", err)
return
}

expected := &SimpleResponse{
Result: "success",
}

if !reflect.DeepEqual(got, expected) {
t.Errorf("Expected %+v, got %+v", expected, got)
}
}

0 comments on commit 80081fe

Please sign in to comment.