Skip to content

Commit 0ad69e1

Browse files
committed
fixup
1 parent 23ad32d commit 0ad69e1

14 files changed

+327
-59
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

+21-26
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,7 +361,13 @@ func (c *client) DisposeIsoNetResources(
372361
if err := c.RemoveClusterTagFromNetwork(csCluster, *isoNet.Network()); err != nil {
373362
return err
374363
}
375-
err := c.DeleteNetworkIfNotInUse(*isoNet.Network())
364+
if err := c.RemoveClusterTagFromVPC(csCluster, isoNet.Spec.VPC); err != nil {
365+
return err
366+
}
367+
if err := c.DeleteNetworkIfNotInUse(*isoNet.Network()); err != nil {
368+
return err
369+
}
370+
err := c.DeleteVPCIfNotInUse(isoNet.Spec.VPC)
376371
return err
377372
}
378373

pkg/cloud/vpc.go

+62-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,15 @@ 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
3639
}
3740

3841
// getVPCOfferingID fetches a vpc offering id.
@@ -65,6 +68,7 @@ func (c *client) ResolveVPC(vpc *infrav1.VPC) error {
6568
return errors.Errorf("no VPC found with ID %s", vpc.ID)
6669
}
6770
vpc.Name = resp.Name
71+
vpc.CIDR = resp.Cidr
6872
return nil
6973
}
7074

@@ -78,6 +82,7 @@ func (c *client) ResolveVPC(vpc *infrav1.VPC) error {
7882
return errors.Errorf("no VPC found with name %s", vpc.Name)
7983
}
8084
vpc.ID = resp.Id
85+
vpc.CIDR = resp.Cidr
8186
return nil
8287
}
8388

@@ -93,12 +98,67 @@ func (c *client) CreateVPC(fd *infrav1.CloudStackFailureDomain, vpc *infrav1.VPC
9398
}
9499

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

pkg/cloud/vpc_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ var _ = ginkgo.Describe("VPC", func() {
3434
mockCtrl *gomock.Controller
3535
mockClient *csapi.CloudStackClient
3636
vs *csapi.MockVPCServiceIface
37+
rs *csapi.MockResourcetagsServiceIface
3738
client cloud.Client
3839
)
3940

@@ -42,6 +43,7 @@ var _ = ginkgo.Describe("VPC", func() {
4243
mockCtrl = gomock.NewController(ginkgo.GinkgoT())
4344
mockClient = csapi.NewMockClient(mockCtrl)
4445
vs = mockClient.VPC.(*csapi.MockVPCServiceIface)
46+
rs = mockClient.Resourcetags.(*csapi.MockResourcetagsServiceIface)
4547
client = cloud.NewClientFromCSAPIClient(mockClient, nil)
4648
dummies.SetDummyVars()
4749
})
@@ -166,6 +168,8 @@ var _ = ginkgo.Describe("VPC", func() {
166168
vs.EXPECT().GetVPCOfferingID(cloud.VPCOffering).Return(offeringID, 1, nil)
167169
vs.EXPECT().NewCreateVPCParams(dummyVPC.CIDR, dummyVPC.Name, dummyVPC.Name, offeringID, dummyFD.Spec.Zone.ID).Return(createVPCParams)
168170
vs.EXPECT().CreateVPC(createVPCParams).Return(createVPCResponse, nil)
171+
rs.EXPECT().NewCreateTagsParams(gomock.Any(), gomock.Any(), gomock.Any()).Return(&csapi.CreateTagsParams{})
172+
rs.EXPECT().CreateTags(gomock.Any()).Return(&csapi.CreateTagsResponse{}, nil)
169173

170174
gomega.Ω(client.CreateVPC(&dummyFD, &dummyVPC)).Should(gomega.Succeed())
171175
gomega.Ω(dummyVPC.ID).Should(gomega.Equal("vpc-123"))

test/e2e/common.go

+13
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,19 @@ func CheckNetworkExists(client *cloudstack.CloudStackClient, networkName string)
393393
return count == 1, nil
394394
}
395395

396+
func CheckVPCExists(client *cloudstack.CloudStackClient, vpcName string) (bool, error) {
397+
_, count, err := client.VPC.GetVPCByName(vpcName)
398+
if err != nil {
399+
if strings.Contains(err.Error(), "No match found for") {
400+
return false, nil
401+
}
402+
return false, err
403+
} else if count > 1 {
404+
return false, fmt.Errorf("Expected 0-1 VPC with name %s, but got %d.", vpcName, count)
405+
}
406+
return count == 1, nil
407+
}
408+
396409
func CreateCloudStackClient(ctx context.Context, kubeConfigPath string) *cloudstack.CloudStackClient {
397410
By("Getting a CloudStack client secret")
398411
secret := &corev1.Secret{}

0 commit comments

Comments
 (0)