Skip to content

Commit 53f6a19

Browse files
committed
fixup
1 parent 23ad32d commit 53f6a19

14 files changed

+318
-34
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

+14-1
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,13 @@ func (c *client) GetOrCreateIsolatedNetwork(
311311
return errors.Wrapf(err, "tagging network with id %s", networkID)
312312
}
313313

314+
// Tag the created VPC.
315+
if net.VPC != nil && net.VPC.ID != "" {
316+
if err := c.AddClusterTag(ResourceTypeVPC, net.VPC.ID, csCluster); err != nil {
317+
return errors.Wrapf(err, "tagging VPC with id %s", net.VPC.ID)
318+
}
319+
}
320+
314321
// Associate Public IP with CloudStackIsolatedNetwork
315322
if err := c.AssociatePublicIPAddress(fd, isoNet, csCluster); err != nil {
316323
return errors.Wrapf(err, "associating public IP address to csCluster")
@@ -372,7 +379,13 @@ func (c *client) DisposeIsoNetResources(
372379
if err := c.RemoveClusterTagFromNetwork(csCluster, *isoNet.Network()); err != nil {
373380
return err
374381
}
375-
err := c.DeleteNetworkIfNotInUse(*isoNet.Network())
382+
if err := c.RemoveClusterTagFromVPC(csCluster, isoNet.Spec.VPC); err != nil {
383+
return err
384+
}
385+
if err := c.DeleteNetworkIfNotInUse(*isoNet.Network()); err != nil {
386+
return err
387+
}
388+
err := c.DeleteVPCIfNotInUse(isoNet.Spec.VPC)
376389
return err
377390
}
378391

pkg/cloud/vpc.go

+60-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.
@@ -93,12 +96,67 @@ func (c *client) CreateVPC(fd *infrav1.CloudStackFailureDomain, vpc *infrav1.VPC
9396
}
9497

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

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{}

test/e2e/config/cloudstack.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ providers:
8888
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-resource-cleanup.yaml"
8989
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-second-cluster.yaml"
9090
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-shared-network-kubevip.yaml"
91+
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-vpc-network.yaml"
9192
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering.yaml"
9293
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-non-customized.yaml"
9394
- sourcePath: "../data/infrastructure-cloudstack/v1beta3/cluster-template-invalid-disk-offering-size-for-customized.yaml"
@@ -129,6 +130,10 @@ variables:
129130
CLOUDSTACK_DOMAIN_NAME: ROOT
130131
CLOUDSTACK_INVALID_DOMAIN_NAME: domainXXXX
131132
CLOUDSTACK_NETWORK_NAME: isolated-for-e2e-1
133+
CLOUDSTACK_VPC_NAME: vpc-for-e2e-1
134+
CLOUDSTACK_VPC_CIDR: 10.10.0.0/16
135+
CLOUDSTACK_GATEWAY: 10.10.0.1
136+
CLOUDSTACK_NETMASK: 255.255.255.0
132137
CLOUDSTACK_NEW_NETWORK_NAME: isolated-for-e2e-new
133138
CLOUDSTACK_SHARED_NETWORK_NAME: Shared1
134139
CLUSTER_ENDPOINT_IP: 172.16.2.199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta3
3+
kind: CloudStackCluster
4+
metadata:
5+
name: ${CLUSTER_NAME}
6+
spec:
7+
failureDomains:
8+
- name: ${CLOUDSTACK_FD1_NAME}
9+
acsEndpoint:
10+
name: ${CLOUDSTACK_FD1_SECRET_NAME}
11+
namespace: default
12+
zone:
13+
name: ${CLOUDSTACK_ZONE_NAME}
14+
network:
15+
name: ${CLOUDSTACK_NETWORK_NAME}
16+
gateway: ${CLOUDSTACK_GATEWAY}
17+
netmask: ${CLOUDSTACK_NETMASK}
18+
vpc:
19+
name: ${CLOUDSTACK_VPC_NAME}
20+
cidr: ${CLOUDSTACK_VPC_CIDR}
21+
controlPlaneEndpoint:
22+
host: ""
23+
port: 6443
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
bases:
2+
- ../bases/cluster-with-kcp.yaml
3+
- ../bases/md.yaml
4+
5+
patchesStrategicMerge:
6+
- ./cluster-with-vpc-network.yaml

0 commit comments

Comments
 (0)