@@ -2,6 +2,7 @@ package client
22
33import (
44 "fmt"
5+ "net"
56 "os"
67 "testing"
78 "time"
@@ -49,6 +50,88 @@ func setUpClient(t *testing.T) *cloudconnexa.Client {
4950 return client
5051}
5152
53+ // cidrOverlaps returns true if two IPv4 networks overlap
54+ func cidrOverlaps (a * net.IPNet , b * net.IPNet ) bool {
55+ return a .Contains (b .IP ) || b .Contains (a .IP )
56+ }
57+
58+ // parseCIDROrNil parses CIDR string and returns *net.IPNet or nil on error
59+ func parseCIDROrNil (cidr string ) * net.IPNet {
60+ _ , ipnet , err := net .ParseCIDR (cidr )
61+ if err != nil {
62+ return nil
63+ }
64+ return ipnet
65+ }
66+
67+ // findAvailableIPv4Subnet scans existing networks' routes and system subnets
68+ // and returns an available 10.a.b.0/24 subnet that does not overlap
69+ func findAvailableIPv4Subnet (c * cloudconnexa.Client ) (string , error ) {
70+ networks , err := c .Networks .List ()
71+ if err != nil {
72+ return "" , err
73+ }
74+
75+ var used []* net.IPNet
76+ for _ , n := range networks {
77+ for _ , r := range n .Routes {
78+ if r .Subnet == "" {
79+ continue
80+ }
81+ if ipn := parseCIDROrNil (r .Subnet ); ipn != nil {
82+ used = append (used , ipn )
83+ }
84+ }
85+ for _ , s := range n .SystemSubnets {
86+ if ipn := parseCIDROrNil (s ); ipn != nil {
87+ used = append (used , ipn )
88+ }
89+ }
90+ }
91+
92+ // Prefer high 10.200.0.0/16 space, then sweep remaining 10.0.0.0/8
93+ for a := 200 ; a <= 254 ; a ++ {
94+ for b := 0 ; b <= 255 ; b ++ {
95+ candidate := fmt .Sprintf ("10.%d.%d.0/24" , a , b )
96+ _ , ipn , err := net .ParseCIDR (candidate )
97+ if err != nil {
98+ continue
99+ }
100+ overlap := false
101+ for _ , u := range used {
102+ if cidrOverlaps (ipn , u ) {
103+ overlap = true
104+ break
105+ }
106+ }
107+ if ! overlap {
108+ return candidate , nil
109+ }
110+ }
111+ }
112+ for a := 0 ; a <= 199 ; a ++ {
113+ for b := 0 ; b <= 255 ; b ++ {
114+ candidate := fmt .Sprintf ("10.%d.%d.0/24" , a , b )
115+ _ , ipn , err := net .ParseCIDR (candidate )
116+ if err != nil {
117+ continue
118+ }
119+ overlap := false
120+ for _ , u := range used {
121+ if cidrOverlaps (ipn , u ) {
122+ overlap = true
123+ break
124+ }
125+ }
126+ if ! overlap {
127+ return candidate , nil
128+ }
129+ }
130+ }
131+
132+ return "" , fmt .Errorf ("no available /24 subnet found in 10.0.0.0/8" )
133+ }
134+
52135// TestListNetworks tests the retrieval of networks using pagination
53136// It verifies that networks can be retrieved successfully
54137func TestListNetworks (t * testing.T ) {
@@ -115,10 +198,12 @@ func TestCreateNetwork(t *testing.T) {
115198 Name : testName ,
116199 VpnRegionID : "it-mxp" ,
117200 }
201+ subnet , err := findAvailableIPv4Subnet (c )
202+ require .NoError (t , err )
118203 route := cloudconnexa.Route {
119204 Description : "test" ,
120205 Type : "IP_V4" ,
121- Subnet : fmt . Sprintf ( "10.%d.%d.0/24" , timestamp % 256 , ( timestamp / 256 ) % 256 ) ,
206+ Subnet : subnet ,
122207 }
123208 network := cloudconnexa.Network {
124209 Description : "test" ,
@@ -131,6 +216,8 @@ func TestCreateNetwork(t *testing.T) {
131216 response , err := c .Networks .Create (network )
132217 require .NoError (t , err )
133218 fmt .Printf ("created %s network\n " , response .ID )
219+ // Ensure cleanup even if subsequent steps fail
220+ defer func () { _ = c .Networks .Delete (response .ID ) }()
134221 test , err := c .Routes .Create (response .ID , route )
135222 require .NoError (t , err )
136223 fmt .Printf ("created %s route\n " , test .ID )
@@ -139,7 +226,7 @@ func TestCreateNetwork(t *testing.T) {
139226 }
140227 ipServiceRoute := cloudconnexa.IPServiceRoute {
141228 Description : "test" ,
142- Value : fmt . Sprintf ( "10.%d.%d.0/24" , timestamp % 256 , ( timestamp / 256 ) % 256 ) ,
229+ Value : subnet ,
143230 }
144231 service := cloudconnexa.IPService {
145232 Name : testName ,
0 commit comments