Skip to content

Commit 6c03dde

Browse files
committed
Add tests for vpc
1 parent d7c9476 commit 6c03dde

File tree

17 files changed

+610
-74
lines changed

17 files changed

+610
-74
lines changed

api/v1beta1/conversion.go

+11
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,14 @@ func GetK8sSecret(name, namespace string) (*corev1.Secret, error) {
174174
}
175175
return endpointCredentials, nil
176176
}
177+
178+
// Convert_v1beta3_Network_To_v1beta1_Network converts from v1beta3.Network to v1beta1.Network
179+
//
180+
//nolint:golint,revive,stylecheck
181+
func Convert_v1beta3_Network_To_v1beta1_Network(in *v1beta3.Network, out *Network, _ conv.Scope) error {
182+
out.ID = in.ID
183+
out.Type = in.Type
184+
out.Name = in.Name
185+
// Skip Gateway, Netmask, and VPC fields as they do not exist in v1beta1.Network
186+
return nil
187+
}

api/v1beta1/zz_generated.conversion.go

+5-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/v1beta2/conversion.go

+28
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,31 @@ limitations under the License.
1515
*/
1616

1717
package v1beta2
18+
19+
import (
20+
conv "k8s.io/apimachinery/pkg/conversion"
21+
"sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3"
22+
)
23+
24+
// Convert_v1beta3_Network_To_v1beta2_Network converts from v1beta3.Network to v1beta2.Network
25+
//
26+
//nolint:golint,revive,stylecheck
27+
func Convert_v1beta3_Network_To_v1beta2_Network(in *v1beta3.Network, out *Network, _ conv.Scope) error {
28+
out.ID = in.ID
29+
out.Type = in.Type
30+
out.Name = in.Name
31+
// Skip Gateway, Netmask, and VPC fields as they do not exist in v1beta2.Network
32+
return nil
33+
}
34+
35+
// Convert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec converts from v1beta3.CloudStackIsolatedNetworkSpec to v1beta2.CloudStackIsolatedNetworkSpec
36+
//
37+
//nolint:golint,revive,stylecheck
38+
func Convert_v1beta3_CloudStackIsolatedNetworkSpec_To_v1beta2_CloudStackIsolatedNetworkSpec(in *v1beta3.CloudStackIsolatedNetworkSpec, out *CloudStackIsolatedNetworkSpec, _ conv.Scope) error {
39+
out.Name = in.Name
40+
out.ID = in.ID
41+
out.ControlPlaneEndpoint = in.ControlPlaneEndpoint
42+
out.FailureDomainName = in.FailureDomainName
43+
// Skip Gateway, Netmask, and VPC fields as they do not exist in v1beta2.CloudStackIsolatedNetworkSpec
44+
return nil
45+
}

api/v1beta2/zz_generated.conversion.go

+10-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controllers/cloudstackfailuredomain_controller.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ func (r *CloudStackFailureDomainReconciliationRunner) Reconcile() (retRes ctrl.R
107107
r.ReconciliationSubject.Spec.Zone.Network.Type == infrav1.NetworkTypeIsolated {
108108
netName := r.ReconciliationSubject.Spec.Zone.Network.Name
109109
if res, err := r.GenerateIsolatedNetwork(
110-
netName, func() string { return r.ReconciliationSubject.Spec.Name }, r.ReconciliationSubject.Spec.Zone.Network)(); r.ShouldReturn(res, err) {
110+
netName,
111+
func() string { return r.ReconciliationSubject.Spec.Name },
112+
r.ReconciliationSubject.Spec.Zone.Network)(); r.ShouldReturn(res, err) {
111113
return res, err
112114
} else if res, err := r.GetObjectByName(r.IsoNetMetaName(netName), r.IsoNet)(); r.ShouldReturn(res, err) {
113115
return res, err

pkg/cloud/isolated_network.go

+26-27
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ func (c *client) CreateIsolatedNetwork(fd *infrav1.CloudStackFailureDomain, isoN
143143
return errors.Wrapf(err, "creating network with name %s", isoNet.Spec.Name)
144144
}
145145
isoNet.Spec.ID = resp.Id
146+
isoNet.Spec.Gateway = resp.Gateway
147+
isoNet.Spec.Netmask = resp.Netmask
146148
return c.AddCreatedByCAPCTag(ResourceTypeNetwork, isoNet.Spec.ID)
147149
}
148150

@@ -204,31 +206,6 @@ func (c *client) GetPublicIP(
204206
return nil, errors.New("no public addresses found in available networks")
205207
}
206208

207-
// GetIsolatedNetwork gets an isolated network in the relevant Zone.
208-
func (c *client) GetIsolatedNetwork(isoNet *infrav1.CloudStackIsolatedNetwork) (retErr error) {
209-
netDetails, count, err := c.cs.Network.GetNetworkByName(isoNet.Spec.Name, cloudstack.WithProject(c.user.Project.ID))
210-
if err != nil {
211-
c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err)
212-
retErr = multierror.Append(retErr, errors.Wrapf(err, "could not get Network ID from %s", isoNet.Spec.Name))
213-
} else if count != 1 {
214-
retErr = multierror.Append(retErr, errors.Errorf(
215-
"expected 1 Network with name %s, but got %d", isoNet.Name, count))
216-
} else { // Got netID from the network's name.
217-
isoNet.Spec.ID = netDetails.Id
218-
return nil
219-
}
220-
221-
netDetails, count, err = c.cs.Network.GetNetworkByID(isoNet.Spec.ID, cloudstack.WithProject(c.user.Project.ID))
222-
if err != nil {
223-
c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err)
224-
return multierror.Append(retErr, errors.Wrapf(err, "could not get Network by ID %s", isoNet.Spec.ID))
225-
} else if count != 1 {
226-
return multierror.Append(retErr, errors.Errorf("expected 1 Network with UUID %s, but got %d", isoNet.Spec.ID, count))
227-
}
228-
isoNet.Name = netDetails.Name
229-
return nil
230-
}
231-
232209
// ResolveLoadBalancerRuleDetails resolves the details of a load balancer rule by PublicIPID and Port.
233210
func (c *client) ResolveLoadBalancerRuleDetails(
234211
isoNet *infrav1.CloudStackIsolatedNetwork,
@@ -303,6 +280,11 @@ func (c *client) GetOrCreateIsolatedNetwork(
303280
}
304281
} else { // Network existed and was resolved. Set ID on isoNet CloudStackIsolatedNetwork in case it only had name set.
305282
isoNet.Spec.ID = net.ID
283+
isoNet.Spec.Gateway = net.Gateway
284+
isoNet.Spec.Netmask = net.Netmask
285+
if net.VPC != nil && net.VPC.ID != "" {
286+
isoNet.Spec.VPC = net.VPC
287+
}
306288
}
307289

308290
// Tag the created network.
@@ -311,6 +293,13 @@ func (c *client) GetOrCreateIsolatedNetwork(
311293
return errors.Wrapf(err, "tagging network with id %s", networkID)
312294
}
313295

296+
// Tag the created VPC.
297+
if net.VPC != nil && net.VPC.ID != "" {
298+
if err := c.AddClusterTag(ResourceTypeVPC, net.VPC.ID, csCluster); err != nil {
299+
return errors.Wrapf(err, "tagging VPC with id %s", net.VPC.ID)
300+
}
301+
}
302+
314303
// Associate Public IP with CloudStackIsolatedNetwork
315304
if err := c.AssociatePublicIPAddress(fd, isoNet, csCluster); err != nil {
316305
return errors.Wrapf(err, "associating public IP address to csCluster")
@@ -372,8 +361,18 @@ func (c *client) DisposeIsoNetResources(
372361
if err := c.RemoveClusterTagFromNetwork(csCluster, *isoNet.Network()); err != nil {
373362
return err
374363
}
375-
err := c.DeleteNetworkIfNotInUse(*isoNet.Network())
376-
return err
364+
if err := c.DeleteNetworkIfNotInUse(*isoNet.Network()); err != nil && !strings.Contains(strings.ToLower(err.Error()), "no match found") {
365+
return err
366+
}
367+
if isoNet.Spec.VPC != nil && isoNet.Spec.VPC.ID != "" {
368+
if err := c.RemoveClusterTagFromVPC(csCluster, *isoNet.Spec.VPC); err != nil {
369+
return err
370+
}
371+
if err := c.DeleteVPCIfNotInUse(*isoNet.Spec.VPC); err != nil && !strings.Contains(strings.ToLower(err.Error()), "no match found") {
372+
return err
373+
}
374+
}
375+
return nil
377376
}
378377

379378
// DeleteNetworkIfNotInUse deletes an isolated network if the network is no longer in use (indicated by in use tags).

pkg/cloud/vpc.go

+58-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package cloud
1818

1919
import (
20+
"strings"
21+
2022
infrav1 "sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3"
2123

2224
"github.com/apache/cloudstack-go/v2/cloudstack"
@@ -25,14 +27,16 @@ import (
2527

2628
// ResourceTypeVPC is the type identifier for VPC resources.
2729
const (
28-
ResourceTypeVPC = "VPC"
30+
ResourceTypeVPC = "Vpc"
2931
VPCOffering = "Default VPC offering"
3032
)
3133

3234
// VPCIface defines the interface for VPC operations.
3335
type VPCIface interface {
3436
ResolveVPC(*infrav1.VPC) error
3537
CreateVPC(*infrav1.CloudStackFailureDomain, *infrav1.VPC) error
38+
RemoveClusterTagFromVPC(*infrav1.CloudStackCluster, infrav1.VPC) error
39+
DeleteVPCIfNotInUse(infrav1.VPC) (retError error)
3640
}
3741

3842
// getVPCOfferingID fetches a vpc offering id.
@@ -65,6 +69,7 @@ func (c *client) ResolveVPC(vpc *infrav1.VPC) error {
6569
return errors.Errorf("no VPC found with ID %s", vpc.ID)
6670
}
6771
vpc.Name = resp.Name
72+
vpc.CIDR = resp.Cidr
6873
return nil
6974
}
7075

@@ -78,6 +83,7 @@ func (c *client) ResolveVPC(vpc *infrav1.VPC) error {
7883
return errors.Errorf("no VPC found with name %s", vpc.Name)
7984
}
8085
vpc.ID = resp.Id
86+
vpc.CIDR = resp.Cidr
8187
return nil
8288
}
8389

@@ -93,12 +99,62 @@ func (c *client) CreateVPC(fd *infrav1.CloudStackFailureDomain, vpc *infrav1.VPC
9399
}
94100

95101
p := c.cs.VPC.NewCreateVPCParams(vpc.CIDR, vpc.Name, vpc.Name, offeringID, fd.Spec.Zone.ID)
96-
102+
setIfNotEmpty(c.user.Project.ID, p.SetProjectid)
103+
p.SetStart(true)
97104
resp, err := c.cs.VPC.CreateVPC(p)
98105
if err != nil {
99106
c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err)
100107
return errors.Wrapf(err, "creating VPC with name %s", vpc.Name)
101108
}
102109
vpc.ID = resp.Id
110+
return c.AddCreatedByCAPCTag(ResourceTypeVPC, vpc.ID)
111+
}
112+
113+
// DeleteVPC deletes a VPC.
114+
func (c *client) DeleteVPC(vpc infrav1.VPC) error {
115+
_, err := c.cs.VPC.DeleteVPC(c.cs.VPC.NewDeleteVPCParams(vpc.ID))
116+
c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err)
117+
return errors.Wrapf(err, "deleting vpc with id %s", vpc.ID)
118+
}
119+
120+
// DeleteVPCIfNotInUse deletes a VPC if the VPC is no longer in use (indicated by in use tags).
121+
func (c *client) DeleteVPCIfNotInUse(vpc infrav1.VPC) (retError error) {
122+
tags, err := c.GetTags(ResourceTypeVPC, vpc.ID)
123+
if err != nil {
124+
return err
125+
}
126+
127+
var clusterTagCount int
128+
for tagName := range tags {
129+
if strings.HasPrefix(tagName, ClusterTagNamePrefix) {
130+
clusterTagCount++
131+
}
132+
}
133+
134+
if clusterTagCount == 0 && tags[CreatedByCAPCTagName] != "" {
135+
return c.DeleteVPC(vpc)
136+
}
137+
138+
return nil
139+
}
140+
141+
func generateVPCTagName(csCluster *infrav1.CloudStackCluster) string {
142+
return ClusterTagNamePrefix + string(csCluster.UID)
143+
}
144+
145+
// RemoveClusterTagFromVPC removes the cluster in use tag from a VPC.
146+
func (c *client) RemoveClusterTagFromVPC(csCluster *infrav1.CloudStackCluster, vpc infrav1.VPC) (retError error) {
147+
tags, err := c.GetTags(ResourceTypeVPC, vpc.ID)
148+
if err != nil {
149+
return err
150+
}
151+
152+
ClusterTagName := generateVPCTagName(csCluster)
153+
if tagValue := tags[ClusterTagName]; tagValue != "" {
154+
if err = c.DeleteTags(ResourceTypeVPC, vpc.ID, map[string]string{ClusterTagName: tagValue}); err != nil {
155+
return err
156+
}
157+
}
158+
103159
return nil
104160
}

0 commit comments

Comments
 (0)