Skip to content

Commit 7624903

Browse files
authored
Merge pull request #25 from cloudscale-ch/image-management
Implement custom image API in go-sdk.
2 parents 834c584 + 8ba3fdb commit 7624903

16 files changed

+845
-57
lines changed

.github/workflows/test.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- name: Set up Go
2020
uses: actions/[email protected]
2121
with:
22-
go-version: '1.15'
22+
go-version: '1.16'
2323
id: go
2424

2525
- name: Check out code into the Go module directory
@@ -34,6 +34,10 @@ jobs:
3434
run: |
3535
make test
3636
37+
- name: Sleep random seconds to ensure test call are not executed at exactly the same time (push and pr runs)
38+
run: sleep $(( ( RANDOM % 30 ) + 1 ))
39+
shell: bash
40+
3741
- name: Run integration tests
3842
timeout-minutes: 60
3943
env:

cloudscale.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,16 @@ type Client struct {
3434
// User agent for client
3535
UserAgent string
3636

37-
Regions RegionService
38-
Servers ServerService
39-
Volumes VolumeService
40-
Networks NetworkService
41-
Subnets SubnetService
42-
FloatingIPs FloatingIPsService
43-
ServerGroups ServerGroupService
44-
ObjectsUsers ObjectsUsersService
37+
Regions RegionService
38+
Servers ServerService
39+
Volumes VolumeService
40+
Networks NetworkService
41+
Subnets SubnetService
42+
FloatingIPs FloatingIPsService
43+
ServerGroups ServerGroupService
44+
ObjectsUsers ObjectsUsersService
45+
CustomImages CustomImageService
46+
CustomImageImports CustomImageImportsService
4547
}
4648

4749
// NewClient returns a new CloudScale API client.
@@ -68,6 +70,8 @@ func NewClient(httpClient *http.Client) *Client {
6870
c.Volumes = VolumeServiceOperations{client: c}
6971
c.ServerGroups = ServerGroupServiceOperations{client: c}
7072
c.ObjectsUsers = ObjectsUsersServiceOperations{client: c}
73+
c.CustomImages = CustomImageServiceOperations{client: c}
74+
c.CustomImageImports = CustomImageImportsServiceOperations{client: c}
7175

7276
return c
7377
}

custom_image_imports.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package cloudscale
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
)
8+
9+
const customImageImportsBasePath = "v1/custom-images/import"
10+
11+
type CustomImageStub struct {
12+
HREF string `json:"href,omitempty"`
13+
UUID string `json:"uuid,omitempty"`
14+
Name string `json:"name,omitempty"`
15+
}
16+
17+
type CustomImageImport struct {
18+
TaggedResource
19+
// Just use omitempty everywhere. This makes it easy to use restful. Errors
20+
// will be coming from the API if something is disabled.
21+
HREF string `json:"href,omitempty"`
22+
UUID string `json:"uuid,omitempty"`
23+
CustomImage CustomImageStub `json:"custom_image,omitempty"`
24+
URL string `json:"url,omitempty"`
25+
Status string `json:"status,omitempty"`
26+
ErrorMessage string `json:"error_message,omitempty"`
27+
}
28+
29+
type CustomImageImportRequest struct {
30+
TaggedResourceRequest
31+
URL string `json:"url,omitempty"`
32+
Name string `json:"name,omitempty"`
33+
Slug string `json:"slug,omitempty"`
34+
UserDataHandling string `json:"user_data_handling,omitempty"`
35+
SourceFormat string `json:"source_format,omitempty"`
36+
Zones []string `json:"zones,omitempty"`
37+
}
38+
39+
type CustomImageImportsService interface {
40+
Create(ctx context.Context, createRequest *CustomImageImportRequest) (*CustomImageImport, error)
41+
Get(ctx context.Context, CustomImageImportID string) (*CustomImageImport, error)
42+
List(ctx context.Context, modifiers ...ListRequestModifier) ([]CustomImageImport, error)
43+
}
44+
45+
type CustomImageImportsServiceOperations struct {
46+
client *Client
47+
}
48+
49+
func (s CustomImageImportsServiceOperations) Create(ctx context.Context, createRequest *CustomImageImportRequest) (*CustomImageImport, error) {
50+
path := customImageImportsBasePath
51+
52+
req, err := s.client.NewRequest(ctx, http.MethodPost, path, createRequest)
53+
if err != nil {
54+
return nil, err
55+
}
56+
57+
customImageImport := new(CustomImageImport)
58+
59+
err = s.client.Do(ctx, req, customImageImport)
60+
if err != nil {
61+
return nil, err
62+
}
63+
64+
return customImageImport, nil
65+
}
66+
67+
func (s CustomImageImportsServiceOperations) Get(ctx context.Context, CustomImageImportID string) (*CustomImageImport, error) {
68+
path := fmt.Sprintf("%s/%s", customImageImportsBasePath, CustomImageImportID)
69+
70+
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
customImageImport := new(CustomImageImport)
76+
err = s.client.Do(ctx, req, customImageImport)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
return customImageImport, nil
82+
}
83+
84+
func (s CustomImageImportsServiceOperations) List(ctx context.Context, modifiers ...ListRequestModifier) ([]CustomImageImport, error) {
85+
path := customImageImportsBasePath
86+
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
for _, modifier := range modifiers {
92+
modifier(req)
93+
}
94+
95+
customImageImports := []CustomImageImport{}
96+
err = s.client.Do(ctx, req, &customImageImports)
97+
if err != nil {
98+
return nil, err
99+
}
100+
101+
return customImageImports, nil
102+
}

custom_image_imports_test.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package cloudscale
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"reflect"
8+
"testing"
9+
)
10+
11+
func TestCustomImageImport_Create(t *testing.T) {
12+
setup()
13+
defer teardown()
14+
15+
CustomImageImportRequest := &CustomImageImportRequest{
16+
Name: "Test Image",
17+
TaggedResourceRequest: TaggedResourceRequest{
18+
TagMap{
19+
"tag": "one",
20+
"other": "tag",
21+
},
22+
},
23+
}
24+
25+
mux.HandleFunc("/v1/custom-images/import", func(w http.ResponseWriter, r *http.Request) {
26+
expected := map[string]interface{}{
27+
"name": "Test Image",
28+
"tags": map[string]interface{}{
29+
"tag": "one",
30+
"other": "tag",
31+
},
32+
}
33+
34+
var v map[string]interface{}
35+
err := json.NewDecoder(r.Body).Decode(&v)
36+
if err != nil {
37+
t.Fatalf("decode json: %v", err)
38+
}
39+
40+
if !reflect.DeepEqual(v, expected) {
41+
t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected)
42+
}
43+
jsonStr := `{
44+
"href": "https://api.cloudscale.ch/v1/custom-images/import/11111111-1864-4608-853a-0771b6885a3a",
45+
"uuid": "11111111-1864-4608-853a-0771b6885a3a",
46+
"custom_image": {
47+
"href": "https://api.cloudscale.ch/v1/custom-images/11111111-1864-4608-853a-0771b6885a3a",
48+
"uuid": "11111111-1864-4608-853a-0771b6885a3a",
49+
"name": "my-foo"
50+
},
51+
"url": "https://example.com/foo.raw",
52+
"status": "in_progress",
53+
"error_message": "",
54+
"tags": {}
55+
}`
56+
fmt.Fprintf(w, jsonStr)
57+
})
58+
59+
customImageImport, err := client.CustomImageImports.Create(ctx, CustomImageImportRequest)
60+
if err != nil {
61+
t.Errorf("CustomImageImport.Create returned error: %v", err)
62+
return
63+
}
64+
65+
expectedUUID := "11111111-1864-4608-853a-0771b6885a3a"
66+
if id := customImageImport.UUID; id != expectedUUID {
67+
t.Errorf("expected id '%s', received '%s'", expectedUUID, id)
68+
}
69+
}
70+
71+
func TestCustomImageImport_Get(t *testing.T) {
72+
setup()
73+
defer teardown()
74+
75+
mux.HandleFunc("/v1/custom-images/import/6fe39134bf4178747eebc429f82cfafdd08891d4279d0d899bc4012db1db6a15", func(w http.ResponseWriter, r *http.Request) {
76+
testHTTPMethod(t, r, http.MethodGet)
77+
fmt.Fprint(w, `{
78+
"href": "https://api.cloudscale.ch/v1/custom-images/import/11111111-1864-4608-853a-0771b6885a3a",
79+
"uuid": "11111111-1864-4608-853a-0771b6885a3a",
80+
"custom_image": {
81+
"href": "https://api.cloudscale.ch/v1/custom-images/11111111-1864-4608-853a-0771b6885a3a",
82+
"uuid": "11111111-1864-4608-853a-0771b6885a3a",
83+
"name": "my-foo"
84+
},
85+
"url": "https://example.com/foo.raw",
86+
"status": "in_progress",
87+
"error_message": "",
88+
"tags": {}
89+
}`)
90+
})
91+
92+
objectUser, err := client.CustomImageImports.Get(ctx, "6fe39134bf4178747eebc429f82cfafdd08891d4279d0d899bc4012db1db6a15")
93+
if err != nil {
94+
t.Errorf("CustomImageImport.Get returned error: %v", err)
95+
}
96+
97+
expected := &CustomImageImport{
98+
HREF: "https://api.cloudscale.ch/v1/custom-images/import/11111111-1864-4608-853a-0771b6885a3a",
99+
UUID: "11111111-1864-4608-853a-0771b6885a3a",
100+
CustomImage: CustomImageStub{
101+
HREF: "https://api.cloudscale.ch/v1/custom-images/11111111-1864-4608-853a-0771b6885a3a",
102+
UUID: "11111111-1864-4608-853a-0771b6885a3a",
103+
Name: "my-foo",
104+
},
105+
URL: "https://example.com/foo.raw",
106+
Status: "in_progress",
107+
ErrorMessage: "",
108+
}
109+
expected.Tags = TagMap{}
110+
111+
if !reflect.DeepEqual(objectUser, expected) {
112+
t.Errorf("CustomImageImport.Get\n got=%#v\nwant=%#v", objectUser, expected)
113+
}
114+
}
115+
116+
func TestCustomImageImport_List(t *testing.T) {
117+
setup()
118+
defer teardown()
119+
120+
mux.HandleFunc("/v1/custom-images/import", func(w http.ResponseWriter, r *http.Request) {
121+
testHTTPMethod(t, r, http.MethodGet)
122+
fmt.Fprint(w, `[{
123+
"href": "https://api.cloudscale.ch/v1/custom-images/import/11111111-1864-4608-853a-0771b6885a3a",
124+
"uuid": "11111111-1864-4608-853a-0771b6885a3a",
125+
"custom_image": {
126+
"href": "https://api.cloudscale.ch/v1/custom-images/11111111-1864-4608-853a-0771b6885a3a",
127+
"uuid": "11111111-1864-4608-853a-0771b6885a3a",
128+
"name": "my-foo"
129+
},
130+
"url": "https://example.com/foo.raw",
131+
"status": "in_progress",
132+
"error_message": "",
133+
"tags": {}
134+
}]`)
135+
})
136+
137+
objectUsers, err := client.CustomImageImports.List(ctx)
138+
if err != nil {
139+
t.Errorf("CustomImageImport.List returned error: %v", err)
140+
}
141+
142+
expectedImage := CustomImageImport{
143+
HREF: "https://api.cloudscale.ch/v1/custom-images/import/11111111-1864-4608-853a-0771b6885a3a",
144+
UUID: "11111111-1864-4608-853a-0771b6885a3a",
145+
CustomImage: CustomImageStub{
146+
HREF: "https://api.cloudscale.ch/v1/custom-images/11111111-1864-4608-853a-0771b6885a3a",
147+
UUID: "11111111-1864-4608-853a-0771b6885a3a",
148+
Name: "my-foo",
149+
},
150+
URL: "https://example.com/foo.raw",
151+
Status: "in_progress",
152+
ErrorMessage: "",
153+
}
154+
expectedImage.Tags = TagMap{}
155+
expected := []CustomImageImport{
156+
expectedImage,
157+
}
158+
if !reflect.DeepEqual(objectUsers, expected) {
159+
t.Errorf("CustomImageImport.List\n got=%#v\nwant=%#v", objectUsers, expected)
160+
}
161+
162+
}

0 commit comments

Comments
 (0)