Skip to content

Commit 248b211

Browse files
committed
Adding Snapshotting Functionality
1 parent a0856c9 commit 248b211

8 files changed

Lines changed: 103 additions & 98 deletions

File tree

.github/workflows/pr-check.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
name: Lint
99
runs-on: ubuntu-20.04
1010
steps:
11-
- uses: actions/checkout@v3
11+
- uses: actions/checkout@v4
1212
- name: golangci-lint
1313
uses: golangci/golangci-lint-action@v3
1414
with:
@@ -25,7 +25,7 @@ jobs:
2525
go-version: "^1.15"
2626

2727
- name: Check out code
28-
uses: actions/checkout@v3
28+
uses: actions/checkout@v4
2929

3030
- name: Cache
3131
uses: actions/cache@v3

.github/workflows/release.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818

1919
steps:
2020
- name: Check out code
21-
uses: actions/checkout@v3
21+
uses: actions/checkout@v4
2222

2323
- name: Cache
2424
uses: actions/cache@v3
@@ -32,7 +32,7 @@ jobs:
3232
run: make container
3333

3434
- name: Log into registry
35-
uses: docker/login-action@v2
35+
uses: docker/login-action@v3
3636
with:
3737
registry: ghcr.io
3838
username: ${{github.actor}}
@@ -77,7 +77,7 @@ jobs:
7777

7878
steps:
7979
- name: Checkout code
80-
uses: actions/checkout@v3
80+
uses: actions/checkout@v4
8181

8282
- name: Create manifest
8383
run: |

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ require (
1717
k8s.io/api v0.28.0
1818
k8s.io/apimachinery v0.28.0
1919
k8s.io/client-go v0.28.0
20-
k8s.io/klog/v2 v2.100.1
2120
k8s.io/mount-utils v0.27.5
2221
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
2322
)
@@ -62,6 +61,7 @@ require (
6261
gopkg.in/warnings.v0 v0.1.2 // indirect
6362
gopkg.in/yaml.v2 v2.4.0 // indirect
6463
gopkg.in/yaml.v3 v3.0.1 // indirect
64+
k8s.io/klog/v2 v2.100.1 // indirect
6565
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
6666
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
6767
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect

go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
9393
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
9494
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
9595
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
96+
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
97+
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
9698
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
9799
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
98100
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
@@ -216,6 +218,8 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
216218
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
217219
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
218220
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
221+
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
222+
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
219223
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
220224
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
221225
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -298,6 +302,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
298302
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
299303
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
300304
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
305+
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
306+
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
301307
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
302308
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
303309
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -313,6 +319,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
313319
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
314320
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
315321
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
322+
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
323+
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
316324
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
317325
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
318326
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

pkg/cloud/cloud.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ type Interface interface {
2323
AttachVolume(ctx context.Context, volumeID, vmID string) (string, error)
2424
DetachVolume(ctx context.Context, volumeID string) error
2525

26-
CreateSnapshot(ctx context.Context, name, volID string) (string, error)
27-
ListSnapshots(filters map[string]string) ([]Snapshot, string, error)
28-
DeleteSnapshot(ctx context.Context, snapID string) error
26+
CreateSnapshot(ctx context.Context, name, volumeID string) (string, error)
27+
ListSnapshots(ctx context.Context, filters map[string]string) ([]Snapshot, string, error)
28+
DeleteSnapshot(ctx context.Context, snapshotID string) error
2929
WaitSnapshotReady(ctx context.Context, snapshotID string) error
3030
GetSnapshotByID(ctx context.Context, snapshotID string) (*Snapshot, error)
3131
GetSnapshotByName(ctx context.Context, name string) (*Snapshot, error)

pkg/cloud/fake/fake.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ func (f *fakeConnector) GetSnapshotByName(ctx context.Context, name string) (*cl
181181
return nil, cloud.ErrNotFound
182182
}
183183

184-
func (f *fakeConnector) ListSnapshots(filters map[string]string) ([]cloud.Snapshot, string, error) {
184+
func (f *fakeConnector) ListSnapshots(ctx context.Context, filters map[string]string) ([]cloud.Snapshot, string, error) {
185185
var snaplist []cloud.Snapshot
186186

187187
name := filters["Name"]

pkg/cloud/snapshots.go

Lines changed: 73 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"google.golang.org/grpc/codes"
1313
"google.golang.org/grpc/status"
1414
"k8s.io/apimachinery/pkg/util/wait"
15-
"k8s.io/klog/v2"
1615
)
1716

1817
const (
@@ -25,121 +24,121 @@ const (
2524

2625
// CreateSnapshot issues a request to take a Snapshot of the specified Volume with the corresponding ID and
2726
// returns the resultant CloudStack Snapshot Item upon success
28-
func (c *client) CreateSnapshot(ctx context.Context, name, volID string) (string, error) {
29-
klog.V(3).Infof("The name of snapshot that is starting to be created is %s and id is %s", name, volID)
30-
27+
func (c *client) CreateSnapshot(ctx context.Context, name, volumeID string) (string, error) {
3128
// Input validation
32-
if name == "" || volID == "" {
33-
return "", status.Errorf(codes.Internal, "Snapshotname %s; requested for volume %s", name, volID)
29+
if name == "" || volumeID == "" {
30+
return "", status.Errorf(codes.Internal, "Snapshotname %s; requested for volume %s", name, volumeID)
3431
}
3532

3633
// Pre-API Call Checks
3734
// Retrieve volume information to ensure it exists and is in a valid state
38-
volume, _, err := c.Volume.GetVolumeByID(volID)
35+
volume, _, err := c.Volume.GetVolumeByID(volumeID)
3936
if err != nil {
40-
return "", fmt.Errorf("failed to retrieve volume '%s': %v", volID, err)
37+
return "", fmt.Errorf("failed to retrieve volume '%s': %v", volumeID, err)
4138
}
4239

4340
// Check if the volume is in a 'Ready' state for snapshot creation
4441
if volume.State != "Ready" {
45-
return "", fmt.Errorf("volume '%s' is not in a 'Ready' state for snapshot creation", volID)
42+
return "", fmt.Errorf("volume '%s' is not in a 'Ready' state for snapshot creation", volumeID)
4643
}
4744

4845
// Preparing snapshot creation parameters
49-
p := c.Snapshot.NewCreateSnapshotParams(volID)
46+
p := c.Snapshot.NewCreateSnapshotParams(volumeID)
5047
p.SetName(name)
51-
p.SetVolumeid(volID)
48+
p.SetVolumeid(volumeID)
5249
p.SetAsyncbackup(asyncBackup)
53-
klog.V(3).Infof("The p is %s ", p)
5450
ctxzap.Extract(ctx).Sugar().Infow("CloudStack API call", "command", "CreateSnapshot", "params", map[string]string{
5551
"name": name,
56-
"volumeid": volID,
52+
"volumeid": volumeID,
5753
"asyncbackup": strconv.FormatBool(asyncBackup),
5854
})
59-
60-
klog.V(3).Infof("snapshot %s of %s has default %s value for asyncBackup", name, volID, asyncBackup)
61-
snap, err := c.Snapshot.CreateSnapshot(p)
55+
snapshot, err := c.Snapshot.CreateSnapshot(p)
6256
if err != nil {
63-
return "", fmt.Errorf("failed to create snapshot '%s' for volume '%s': %v", name, volID, err)
57+
return "", fmt.Errorf("failed to create snapshot '%s' for volume '%s': %v", name, volumeID, err)
6458
}
6559

66-
return snap.Id, nil
60+
return snapshot.Id, nil
6761
}
6862

6963
// ListSnapshots retrieves a list of active snapshots from CloudStack for the corresponding Domain. We also
7064
// provide the ability to provide limit to enable the consumer to provide accurate pagination.
7165
// In addition the filters argument provides a mechanism for passing in valid filter strings to the list
7266
// operation. Valid filter keys are: Name, VolumeID, Limit, Marker (DomainID has no effect)
7367

74-
func (c *client) ListSnapshots(filters map[string]string) ([]Snapshot, string, error) {
75-
var snaps []Snapshot
68+
func (c *client) ListSnapshots(ctx context.Context, filters map[string]string) ([]Snapshot, string, error) {
69+
var snapshotList []Snapshot
7670
var nextPageToken string
7771

7872
// Initialize parameters for CloudStack API call
79-
params := c.Snapshot.NewListSnapshotsParams()
73+
p := c.Snapshot.NewListSnapshotsParams()
74+
75+
// Declare pointer variables for pageSize and currentPage
76+
pageSize := 20 // Default pageSize
77+
currentPage := 1 // Default to the first page
78+
var err error
8079

8180
// Apply filters and pagination parameters
82-
for key, val := range filters {
81+
for key, value := range filters {
8382
switch key {
8483
case "Name":
85-
params.SetName(val)
84+
p.SetName(value)
8685
case "VolumeID":
87-
params.SetVolumeid(val)
86+
p.SetVolumeid(value)
8887
case "Marker":
89-
page, err := strconv.Atoi(val)
88+
// Try to parse the page number from the filters
89+
currentPage, err = strconv.Atoi(value)
9090
if err != nil {
91-
klog.V(3).Infof("Invalid format for Marker: %s, defaulting to first page", val)
92-
page = 1 // Default to the first page if the marker is invalid
91+
fmt.Printf("Invalid format for Marker: %s, using default page\n", value)
92+
currentPage = 1 // Reassign to default if parsing fails
9393
}
94-
params.SetPage(page)
94+
p.SetPage(currentPage)
9595
case "Limit":
96-
limit, err := strconv.Atoi(val)
96+
// Try to parse the page size from the filters
97+
pageSize, err = strconv.Atoi(value)
9798
if err != nil {
98-
klog.V(3).Infof("Invalid format for Limit: %s, defaulting to a preset limit", val)
99-
limit = 20 // Set a default limit if the input is invalid
99+
fmt.Printf("Invalid or unsupported format for Limit: %s, using default limit\n", value)
100+
pageSize = 20 // Reassign to default if parsing fails or unsupported value
100101
}
101-
params.SetPagesize(limit)
102+
p.SetPagesize(pageSize)
102103
default:
103-
klog.V(3).Infof("Not a valid filter key %s", key)
104+
fmt.Printf("Not a valid filter key %s\n", key)
104105
}
105106
}
106107

107108
// Log the final pagination settings
108-
currentPage, _ := params.GetPage()
109-
currentPageSize, _ := params.GetPagesize()
110-
klog.V(3).Infof("Fetching snapshots with Page: %d, PageSize: %d", currentPage, currentPageSize)
109+
fmt.Printf("Fetching snapshots with Page: %d, PageSize: %d\n", currentPage, pageSize)
111110

112111
// Call CloudStack API to list snapshots
113-
resp, err := c.Snapshot.ListSnapshots(params)
112+
resp, err := c.Snapshot.ListSnapshots(p)
114113
if err != nil {
115-
klog.Errorf("Error listing snapshots from CloudStack: %v", err)
116114
return nil, "", fmt.Errorf("error listing snapshots from CloudStack: %w", err)
117115
}
118116

119117
// Convert the response to your []Snapshot type
120-
for _, apiSnap := range resp.Snapshots {
121-
snap := convertAPISnapshotToSnapshot(apiSnap)
122-
snaps = append(snaps, snap)
118+
for _, apiSnapshot := range resp.Snapshots {
119+
snapshot := convertAPISnapshotToSnapshot(apiSnapshot)
120+
snapshotList = append(snapshotList, snapshot)
123121
}
124122

125123
// Determine the nextPageToken
126-
if morePagesExist(resp) {
127-
page, pageSet := params.GetPage()
128-
if pageSet {
129-
nextPageToken = strconv.Itoa(page + 1) // Set nextPageToken to the next page
130-
} else {
131-
nextPageToken = "1" // Set to first page if page is not set
124+
if morePagesExist(resp, pageSize, currentPage) {
125+
// Set nextPageToken to the next page
126+
nextPageToken = strconv.Itoa(currentPage + 1)
127+
if nextPageToken != "" {
128+
fmt.Println("The nextPageToken is set, more than one page is available.")
129+
132130
}
131+
133132
}
134133

135-
return snaps, nextPageToken, nil
134+
return snapshotList, nextPageToken, nil
136135
}
137136

138137
// DeleteSnapshot issues a request to delete the Snapshot with the specified ID from the backend
139-
func (c *client) DeleteSnapshot(ctx context.Context, snapID string) error {
140-
p := c.Snapshot.NewDeleteSnapshotParams(snapID)
138+
func (c *client) DeleteSnapshot(ctx context.Context, snapshotID string) error {
139+
p := c.Snapshot.NewDeleteSnapshotParams(snapshotID)
141140
ctxzap.Extract(ctx).Sugar().Infow("CloudStack API call", "command", "DeleteSnapshot", "params", map[string]string{
142-
"id": snapID,
141+
"id": snapshotID,
143142
})
144143
_, err := c.Snapshot.DeleteSnapshot(p)
145144
if err != nil && strings.Contains(err.Error(), "4350") {
@@ -237,29 +236,39 @@ func (c *client) WaitSnapshotReady(ctx context.Context, snapshotID string) error
237236
func (c *client) snapshotIsReady(ctx context.Context, snapshotID string) (bool, error) {
238237
snap, err := c.GetSnapshotByID(ctx, snapshotID)
239238
if err != nil {
240-
klog.Errorf("Snapshot os not ready: %v", err)
241-
return false, err
239+
return false, fmt.Errorf("Snapshot is not ready: %w", err)
242240
}
243241

244242
return snap.State == snapshotReadyStatus, nil
245243
}
246244

247245
// convertAPISnapshotToSnapshot converts an API snapshot object to your application's Snapshot type.
248-
func convertAPISnapshotToSnapshot(apiSnap *cloudstack.Snapshot) Snapshot {
246+
func convertAPISnapshotToSnapshot(apiSnapshot *cloudstack.Snapshot) Snapshot {
249247
// Conversion logic here
250248
return Snapshot{
251-
ID: apiSnap.Id,
252-
Created: apiSnap.Created,
253-
Name: apiSnap.Name,
254-
VolumeID: apiSnap.Volumeid,
255-
ZoneID: apiSnap.Zoneid,
256-
State: apiSnap.State,
257-
VirtualSize: int(apiSnap.Virtualsize),
249+
ID: apiSnapshot.Id,
250+
Created: apiSnapshot.Created,
251+
Name: apiSnapshot.Name,
252+
VolumeID: apiSnapshot.Volumeid,
253+
ZoneID: apiSnapshot.Zoneid,
254+
State: apiSnapshot.State,
255+
VirtualSize: int(apiSnapshot.Virtualsize),
258256
}
259257
}
260258

261259
// morePagesExist determines if there are more pages of results based on the API response.
262-
func morePagesExist(resp *cloudstack.ListSnapshotsResponse) bool {
263-
// Implement logic to determine if more pages exist
264-
return false
260+
func morePagesExist(resp *cloudstack.ListSnapshotsResponse, pageSize int, currentPage int) bool {
261+
if resp == nil || pageSize <= 0 {
262+
return false
263+
}
264+
265+
totalSnapshots := resp.Count
266+
totalPages := totalSnapshots / pageSize
267+
// Check for any remaining snapshots not fitting in a full page
268+
if totalSnapshots%pageSize != 0 {
269+
totalPages++
270+
}
271+
fmt.Printf("Total Page is %d", totalPages)
272+
273+
return currentPage < totalPages
265274
}

0 commit comments

Comments
 (0)