Skip to content

Commit 4da9fd0

Browse files
authored
Merge pull request #18 from alakae/subnet_addresses
Update Subnet and Specify Server Addresses
2 parents 6b1fd51 + c2966fe commit 4da9fd0

File tree

5 files changed

+266
-77
lines changed

5 files changed

+266
-77
lines changed

servers.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,13 @@ type ServerRequest struct {
9797
}
9898

9999
type InterfaceRequest struct {
100-
Network string `json:"network,omitempty"`
101-
Addresses *[]string `json:"addresses,omitempty"`
100+
Network string `json:"network,omitempty"`
101+
Addresses *[]AddressRequest `json:"addresses,omitempty"`
102+
}
103+
104+
type AddressRequest struct {
105+
Subnet string `json:"subnet,omitempty"`
106+
Address string `json:"address,omitempty"`
102107
}
103108

104109
type ServerService interface {

subnets.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ type Subnet struct {
1212
TaggedResource
1313
// Just use omitempty everywhere. This makes it easy to use restful. Errors
1414
// will be coming from the API if something is disabled.
15-
HREF string `json:"href,omitempty"`
16-
UUID string `json:"uuid,omitempty"`
17-
CIDR string `json:"cidr,omitempty"`
18-
Network NetworkStub `json:"network,omitempty"`
15+
HREF string `json:"href,omitempty"`
16+
UUID string `json:"uuid,omitempty"`
17+
CIDR string `json:"cidr,omitempty"`
18+
Network NetworkStub `json:"network,omitempty"`
19+
GatewayAddress string `json:"gateway_address,omitempty"`
20+
DNSServers []string `json:"dns_servers,omitempty"`
1921
}
2022

2123
type SubnetStub struct {
@@ -26,14 +28,23 @@ type SubnetStub struct {
2628

2729
type SubnetCreateRequest struct {
2830
TaggedResourceRequest
29-
CIDR string `json:"cidr,omitempty"`
30-
Network string `json:"network,omitempty"`
31+
CIDR string `json:"cidr,omitempty"`
32+
Network string `json:"network,omitempty"`
33+
GatewayAddress string `json:"gateway_address,omitempty"`
34+
DNSServers []string `json:"dns_servers,omitempty"`
35+
}
36+
37+
type SubnetUpdateRequest struct {
38+
TaggedResourceRequest
39+
GatewayAddress string `json:"gateway_address,omitempty"`
40+
DNSServers []string `json:"dns_servers,omitempty"`
3141
}
3242

3343
type SubnetService interface {
3444
Create(ctx context.Context, createRequest *SubnetCreateRequest) (*Subnet, error)
3545
Get(ctx context.Context, subnetID string) (*Subnet, error)
3646
List(ctx context.Context, modifiers ...ListRequestModifier) ([]Subnet, error)
47+
Update(ctx context.Context, subnetID string, updateRequest *SubnetUpdateRequest) error
3748
Delete(ctx context.Context, subnetID string) error
3849
}
3950

@@ -59,6 +70,22 @@ func (s SubnetServiceOperations) Create(ctx context.Context, createRequest *Subn
5970
return subnet, nil
6071
}
6172

73+
func (f SubnetServiceOperations) Update(ctx context.Context, subnetID string, updateRequest *SubnetUpdateRequest) error {
74+
path := fmt.Sprintf("%s/%s", subnetBasePath, subnetID)
75+
76+
req, err := f.client.NewRequest(ctx, http.MethodPatch, path, updateRequest)
77+
if err != nil {
78+
return err
79+
}
80+
81+
err = f.client.Do(ctx, req, nil)
82+
if err != nil {
83+
return err
84+
}
85+
return nil
86+
}
87+
88+
6289
func (s SubnetServiceOperations) Get(ctx context.Context, subnetID string) (*Subnet, error) {
6390
path := fmt.Sprintf("%s/%s", subnetBasePath, subnetID)
6491

test/integration/network_integration_test.go

Lines changed: 114 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package integration
55
import (
66
"context"
77
"github.com/cloudscale-ch/cloudscale-go-sdk"
8+
"regexp"
89
"sync"
910
"testing"
1011
"time"
@@ -81,48 +82,94 @@ func TestIntegrationNetwork_CreateWithoutSubnet(t *testing.T) {
8182
func TestIntegrationNetwork_CreateAttached(t *testing.T) {
8283
integrationTest(t)
8384

85+
autoCreateSubnet := false
8486
createNetworkRequest := &cloudscale.NetworkCreateRequest{
8587
Name: networkBaseName,
88+
AutoCreateIPV4Subnet: &autoCreateSubnet,
8689
}
87-
8890
network, err := client.Networks.Create(context.TODO(), createNetworkRequest)
8991
if err != nil {
9092
t.Fatalf("Networks.Create returned error %s\n", err)
9193
}
9294

93-
interfaceRequests := []cloudscale.InterfaceRequest{
94-
cloudscale.InterfaceRequest{
95-
Network: network.UUID,
96-
},
97-
}
98-
createServerRequest := &cloudscale.ServerRequest{
99-
Name: "go-sdk-integration-test-network",
100-
Flavor: "flex-2",
101-
Image: DefaultImageSlug,
102-
VolumeSizeGB: 10,
103-
Interfaces: &interfaceRequests,
104-
SSHKeys: []string{
105-
pubKey,
106-
},
95+
createSubnetRequest := &cloudscale.SubnetCreateRequest{
96+
Network: network.UUID,
97+
CIDR: "192.168.42.0/24",
10798
}
108-
109-
server, err := client.Servers.Create(context.Background(), createServerRequest)
99+
subnet, err := client.Subnets.Create(context.TODO(), createSubnetRequest)
110100
if err != nil {
111-
t.Fatalf("Servers.Create returned error %s\n", err)
112-
}
113-
waitUntil("running", server.UUID, t)
114-
115-
if numNetworks := len(server.Interfaces); numNetworks != 1 {
116-
t.Errorf("Attatched to number of Networks\ngot=%#v\nwant=%#v", numNetworks, 1)
117-
}
118-
if singleInterface := server.Interfaces[0]; singleInterface.Network.UUID != network.UUID {
119-
t.Errorf("Attatched to wrong Network\ngot=%#v\nwant=%#v", singleInterface.Network.UUID, network.UUID)
101+
t.Fatalf("Subnets.Create returned error %s\n", err)
102+
}
103+
104+
cases := []struct {
105+
name string
106+
in *[]cloudscale.InterfaceRequest
107+
expectedIP string
108+
}{
109+
{"Attach by network UUID", &[]cloudscale.InterfaceRequest{
110+
{
111+
Network: network.UUID,
112+
},
113+
}, `192\.168\.42\.[0-9]*`},
114+
{"Attach by subnet UUID", &[]cloudscale.InterfaceRequest{
115+
{
116+
Addresses: &[]cloudscale.AddressRequest{
117+
{
118+
Subnet: subnet.UUID,
119+
},
120+
},
121+
},
122+
}, `192\.168\.42\.[0-9]*`},
123+
{"Attach by subnet UUID with predefined IP", &[]cloudscale.InterfaceRequest{
124+
{
125+
Addresses: &[]cloudscale.AddressRequest{
126+
{
127+
Subnet: subnet.UUID,
128+
Address: "192.168.42.242",
129+
},
130+
},
131+
},
132+
}, `192\.168\.42\.242`},
133+
}
134+
135+
for _, tt := range cases {
136+
t.Run(tt.name, func(t *testing.T) {
137+
createServerRequest := &cloudscale.ServerRequest{
138+
Name: "go-sdk-integration-test-network",
139+
Flavor: "flex-2",
140+
Image: DefaultImageSlug,
141+
VolumeSizeGB: 10,
142+
Interfaces: tt.in,
143+
SSHKeys: []string{
144+
pubKey,
145+
},
146+
}
147+
148+
server, err := client.Servers.Create(context.Background(), createServerRequest)
149+
if err != nil {
150+
t.Fatalf("Servers.Create returned error %s\n", err)
151+
}
152+
waitUntil("running", server.UUID, t)
153+
154+
if numNetworks := len(server.Interfaces); numNetworks != 1 {
155+
t.Errorf("Attatched to number of Networks\ngot=%#v\nwant=%#v", numNetworks, 1)
156+
}
157+
singleInterface := server.Interfaces[0];
158+
if singleInterface.Network.UUID != network.UUID {
159+
t.Errorf("Attatched to wrong Network\ngot=%#v\nwant=%#v", singleInterface.Network.UUID, network.UUID)
160+
}
161+
re := regexp.MustCompile(tt.expectedIP)
162+
if !re.Match([]byte(singleInterface.Addresses[0].Address)) {
163+
t.Errorf("Expected IP regex does not match\ngot=%#v\nwant=%#v", singleInterface.Addresses[0].Address, tt.expectedIP)
164+
}
165+
166+
err = client.Servers.Delete(context.Background(), server.UUID)
167+
if err != nil {
168+
t.Fatalf("Servers.Delete returned error %s\n", err)
169+
}
170+
})
120171
}
121172

122-
err = client.Servers.Delete(context.Background(), server.UUID)
123-
if err != nil {
124-
t.Fatalf("Servers.Delete returned error %s\n", err)
125-
}
126173
// sending the next request immediately can cause errors, since the port cleanup process is still ongoing
127174
time.Sleep(5 * time.Second)
128175
err = client.Networks.Delete(context.Background(), network.UUID)
@@ -131,20 +178,37 @@ func TestIntegrationNetwork_CreateAttached(t *testing.T) {
131178
}
132179
}
133180

134-
func TestIntegrationNetwork_AttachWithoutIP(t *testing.T) {
181+
func TestIntegrationNetwork_Reattach(t *testing.T) {
135182
integrationTest(t)
136183

137-
interfaceRequests := []cloudscale.InterfaceRequest{
138-
cloudscale.InterfaceRequest{
139-
Network: "public",
140-
},
184+
autoCreateSubnet := false
185+
createNetworkRequest := &cloudscale.NetworkCreateRequest{
186+
Name: networkBaseName,
187+
AutoCreateIPV4Subnet: &autoCreateSubnet,
188+
}
189+
network, err := client.Networks.Create(context.TODO(), createNetworkRequest)
190+
if err != nil {
191+
t.Fatalf("Networks.Create returned error %s\n", err)
192+
}
193+
194+
createSubnetRequest := &cloudscale.SubnetCreateRequest{
195+
Network: network.UUID,
196+
CIDR: "192.168.77.0/24",
197+
}
198+
subnet, err := client.Subnets.Create(context.TODO(), createSubnetRequest)
199+
if err != nil {
200+
t.Fatalf("Subnets.Create returned error %s\n", err)
201+
}
202+
203+
interfaces := []cloudscale.InterfaceRequest{
204+
{Network: "public"},
141205
}
142206
createServerRequest := &cloudscale.ServerRequest{
143207
Name: "go-sdk-integration-test-network",
144208
Flavor: "flex-2",
145209
Image: DefaultImageSlug,
146210
VolumeSizeGB: 10,
147-
Interfaces: &interfaceRequests,
211+
Interfaces: &interfaces,
148212
SSHKeys: []string{
149213
pubKey,
150214
},
@@ -160,48 +224,34 @@ func TestIntegrationNetwork_AttachWithoutIP(t *testing.T) {
160224
t.Errorf("Attatched to number of Networks\ngot=%#v\nwant=%#v", numNetworks, 1)
161225
}
162226

163-
createNetworkRequest := &cloudscale.NetworkCreateRequest{
164-
Name: networkBaseName,
165-
}
166-
167-
network, err := client.Networks.Create(context.TODO(), createNetworkRequest)
168-
if err != nil {
169-
t.Fatalf("Networks.Create returned error %s\n", err)
170-
}
171-
172-
interfaceRequests = append(interfaceRequests, cloudscale.InterfaceRequest{
173-
Network: network.UUID,
174-
Addresses: &[]string{},
227+
addresses := []cloudscale.AddressRequest{{
228+
Subnet: subnet.UUID,
229+
Address: "192.168.77.77",
230+
}}
231+
interfaces = append(interfaces, cloudscale.InterfaceRequest{
232+
Addresses: &addresses,
175233
})
176-
updateServerRequest := &cloudscale.ServerUpdateRequest{
177-
Interfaces: &interfaceRequests,
234+
updateRequest := cloudscale.ServerUpdateRequest{
235+
Interfaces: &interfaces,
178236
}
179-
180-
err = client.Servers.Update(context.TODO(), server.UUID, updateServerRequest)
237+
err = client.Servers.Update(context.Background(), server.UUID, &updateRequest)
181238
if err != nil {
182-
t.Errorf("Servers.Update returned error: %v", err)
239+
t.Fatalf("Servers.Update returned error %s\n", err)
183240
}
184241

185-
server, err = client.Servers.Get(context.Background(), server.UUID)
242+
updatedServer, err := client.Servers.Get(context.Background(), server.UUID)
186243
if err != nil {
187-
t.Errorf("Server.Get returned error: %v", err)
244+
t.Fatalf("Servers.Get returned error %s\n", err)
188245
}
189-
if numNetworks := len(server.Interfaces); numNetworks != 2 {
246+
if numNetworks := len(updatedServer.Interfaces); numNetworks != 2 {
190247
t.Errorf("Attatched to number of Networks\ngot=%#v\nwant=%#v", numNetworks, 2)
191248
}
192-
second := server.Interfaces[1]
193-
if second.Network.UUID != network.UUID {
194-
t.Errorf("Attatched to wrong Network\ngot=%#v\nwant=%#v", second.Network.UUID, network.UUID)
195-
}
196-
197-
if addressCount := len(second.Addresses); addressCount > 0 {
198-
t.Errorf("Expected no addresses\ngot=%#v", addressCount)
199-
}
200249

201250
err = client.Servers.Delete(context.Background(), server.UUID)
202251
if err != nil {
203252
t.Fatalf("Servers.Delete returned error %s\n", err)
204253
}
254+
205255
// sending the next request immediately can cause errors, since the port cleanup process is still ongoing
206256
time.Sleep(5 * time.Second)
207257
err = client.Networks.Delete(context.Background(), network.UUID)

0 commit comments

Comments
 (0)