Skip to content

Commit 623e447

Browse files
authored
Merge pull request #28 from aws/feature/network-tags
feature/tag-network
2 parents 61b6aa5 + 187d366 commit 623e447

12 files changed

+262
-7
lines changed

api/v1beta1/cloudstackcluster_webhook_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ var _ = Describe("CloudStackCluster webhooks", func() {
3131
clusterKind = "CloudStackCluster"
3232
clusterName = "test-cluster"
3333
clusterNamespace = "default"
34+
clusterId = "0"
3435
identitySecretName = "IdentitySecret"
3536
zone = "Zone"
3637
network = "Network"
@@ -47,6 +48,7 @@ var _ = Describe("CloudStackCluster webhooks", func() {
4748
ObjectMeta: metav1.ObjectMeta{
4849
Name: clusterName,
4950
Namespace: clusterNamespace,
51+
UID: clusterId,
5052
},
5153
Spec: CloudStackClusterSpec{
5254
IdentityRef: &CloudStackIdentityReference{
@@ -72,6 +74,7 @@ var _ = Describe("CloudStackCluster webhooks", func() {
7274
ObjectMeta: metav1.ObjectMeta{
7375
Name: clusterName,
7476
Namespace: clusterNamespace,
77+
UID: clusterId,
7578
},
7679
Spec: CloudStackClusterSpec{
7780
IdentityRef: &CloudStackIdentityReference{
@@ -96,6 +99,7 @@ var _ = Describe("CloudStackCluster webhooks", func() {
9699
ObjectMeta: metav1.ObjectMeta{
97100
Name: clusterName,
98101
Namespace: clusterNamespace,
102+
UID: clusterId,
99103
},
100104
Spec: CloudStackClusterSpec{
101105
IdentityRef: &CloudStackIdentityReference{
@@ -125,6 +129,7 @@ var _ = Describe("CloudStackCluster webhooks", func() {
125129
ObjectMeta: metav1.ObjectMeta{
126130
Name: clusterName,
127131
Namespace: clusterNamespace,
132+
UID: clusterId,
128133
},
129134
Spec: CloudStackClusterSpec{
130135
IdentityRef: &CloudStackIdentityReference{

controllers/cloudstackcluster_controller.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,9 @@ func (r *CloudStackClusterReconciler) reconcileDelete(
145145

146146
log.V(1).Info("reconcileDelete CloudStackCluster...")
147147

148-
// TODO Decide what resources to remove w/Cluster if any.
149-
// cloud.DestroyCluster(r.CS, csStackCluster)
148+
if err := r.CS.DisposeClusterResources(csCluster); err != nil {
149+
return ctrl.Result{}, err
150+
}
150151

151152
controllerutil.RemoveFinalizer(csCluster, infrav1.ClusterFinalizer)
152153
return ctrl.Result{}, nil

controllers/cloudstackcluster_controller_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func getCloudStackCluster() *infrav1.CloudStackCluster {
4040
ObjectMeta: metav1.ObjectMeta{
4141
GenerateName: "cs-cluster-test1-",
4242
Namespace: "default",
43+
UID: "0",
4344
},
4445
Spec: infrav1.CloudStackClusterSpec{
4546
Zone: "zone",

pkg/cloud/affinity_groups_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package cloud_test
1818

1919
import (
2020
"errors"
21-
2221
"github.com/apache/cloudstack-go/v2/cloudstack"
2322
infrav1 "github.com/aws/cluster-api-provider-cloudstack/api/v1beta1"
2423
"github.com/aws/cluster-api-provider-cloudstack/pkg/cloud"
@@ -51,6 +50,7 @@ var _ = Describe("AffinityGroup Unit Tests", func() {
5150
Type: cloud.AffinityGroupType}
5251
cluster = &infrav1.CloudStackCluster{Spec: infrav1.CloudStackClusterSpec{
5352
Zone: "Zone1", Network: "SharedGuestNet1"}}
53+
cluster.ObjectMeta.SetUID("0")
5454
machine = &infrav1.CloudStackMachine{Spec: infrav1.CloudStackMachineSpec{
5555
Offering: "Medium Instance", Template: "Ubuntu20"}}
5656
machine.ObjectMeta.SetName("rejoshed-affinity-group-test-vm")

pkg/cloud/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type Client interface {
3737
ResolveLoadBalancerRuleDetails(*infrav1.CloudStackCluster) error
3838
GetOrCreateLoadBalancerRule(*infrav1.CloudStackCluster) error
3939
AffinityGroupIFace
40+
TagIFace
4041
}
4142

4243
type client struct {

pkg/cloud/cluster.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
type ClusterIface interface {
2626
GetOrCreateCluster(*infrav1.CloudStackCluster) error
27+
DisposeClusterResources(cluster *infrav1.CloudStackCluster) error
2728
}
2829

2930
func (c *client) resolveZone(csCluster *infrav1.CloudStackCluster) (retErr error) {
@@ -92,3 +93,10 @@ func (c *client) GetOrCreateCluster(csCluster *infrav1.CloudStackCluster) (retEr
9293
csCluster.Status.Ready = true
9394
return nil
9495
}
96+
97+
func (c *client) DisposeClusterResources(csCluster *infrav1.CloudStackCluster) (retError error) {
98+
if err := c.RemoveClusterTagFromNetwork(csCluster); err != nil {
99+
return err
100+
}
101+
return c.DeleteNetworkIfNotInUse(csCluster)
102+
}

pkg/cloud/cluster_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package cloud_test
1717

1818
import (
1919
"fmt"
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2021

2122
"github.com/apache/cloudstack-go/v2/cloudstack"
2223
infrav1 "github.com/aws/cluster-api-provider-cloudstack/api/v1beta1"
@@ -58,7 +59,11 @@ var _ = Describe("Cluster", func() {
5859
cluster := &infrav1.CloudStackCluster{
5960
Spec: infrav1.CloudStackClusterSpec{
6061
Zone: zoneName,
61-
Network: netName}}
62+
Network: netName},
63+
ObjectMeta: metav1.ObjectMeta{
64+
UID: "0",
65+
},
66+
}
6267

6368
It("handles zone not found.", func() {
6469
expectedErr := fmt.Errorf("Not found")

pkg/cloud/instance_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package cloud_test
1818

1919
import (
2020
"fmt"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122

2223
capiv1 "sigs.k8s.io/cluster-api/api/v1beta1"
2324

@@ -64,8 +65,9 @@ var _ = Describe("Instance", func() {
6465
Template: "template-name"}}
6566
csMachine.Name = "instance-name"
6667
csCluster = &infrav1.CloudStackCluster{
67-
Spec: infrav1.CloudStackClusterSpec{},
68-
Status: infrav1.CloudStackClusterStatus{ZoneID: "zone-id"}}
68+
Spec: infrav1.CloudStackClusterSpec{},
69+
Status: infrav1.CloudStackClusterStatus{ZoneID: "zone-id"},
70+
ObjectMeta: metav1.ObjectMeta{UID: "0"}}
6971
machine = &capiv1.Machine{}
7072
})
7173

pkg/cloud/network.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,13 @@ func (c *client) ResolveNetwork(csCluster *infrav1.CloudStackCluster) (retErr er
5858
return nil
5959
}
6060

61+
func generateNetworkTagName(csCluster *infrav1.CloudStackCluster) string {
62+
return clusterTagNamePrefix + string(csCluster.UID)
63+
}
64+
6165
func (c *client) GetOrCreateNetwork(csCluster *infrav1.CloudStackCluster) (retErr error) {
6266
if retErr = c.ResolveNetwork(csCluster); retErr == nil { // Found network.
63-
return nil
67+
return addClusterTags(c, csCluster, false)
6468
} else if !strings.Contains(retErr.Error(), "No match found") { // Some other error.
6569
return retErr
6670
} // Network not found.
@@ -86,6 +90,66 @@ func (c *client) GetOrCreateNetwork(csCluster *infrav1.CloudStackCluster) (retEr
8690
csCluster.Status.NetworkID = resp.Id
8791
csCluster.Status.NetworkType = resp.Type
8892

93+
return addClusterTags(c, csCluster, true)
94+
}
95+
96+
func addClusterTags(c *client, csCluster *infrav1.CloudStackCluster, addCreatedByTag bool) error {
97+
clusterTagName := generateNetworkTagName(csCluster)
98+
newTags := map[string]string{}
99+
100+
existingTags, err := c.GetNetworkTags(csCluster.Status.NetworkID)
101+
if err != nil {
102+
return err
103+
}
104+
105+
if existingTags[clusterTagName] == "" {
106+
newTags[clusterTagName] = "1"
107+
}
108+
109+
if addCreatedByTag && existingTags[createdByCapcTagName] == "" {
110+
newTags[createdByCapcTagName] = "1"
111+
}
112+
113+
if len(newTags) > 0 {
114+
return c.AddNetworkTags(csCluster.Status.NetworkID, newTags)
115+
}
116+
117+
return nil
118+
}
119+
120+
func (c *client) RemoveClusterTagFromNetwork(csCluster *infrav1.CloudStackCluster) (retError error) {
121+
tags, err := c.GetNetworkTags(csCluster.Status.NetworkID)
122+
if err != nil {
123+
return err
124+
}
125+
126+
clusterTagName := generateNetworkTagName(csCluster)
127+
if tagValue := tags[clusterTagName]; tagValue != "" {
128+
if err = c.DeleteNetworkTags(csCluster.Status.NetworkID, map[string]string{clusterTagName: tagValue}); err != nil {
129+
return err
130+
}
131+
}
132+
133+
return nil
134+
}
135+
136+
func (c *client) DeleteNetworkIfNotInUse(csCluster *infrav1.CloudStackCluster) (retError error) {
137+
tags, err := c.GetNetworkTags(csCluster.Status.NetworkID)
138+
if err != nil {
139+
return err
140+
}
141+
142+
var clusterTagCount int
143+
for tagName := range tags {
144+
if strings.HasPrefix(tagName, clusterTagNamePrefix) {
145+
clusterTagCount++
146+
}
147+
}
148+
149+
if clusterTagCount == 0 && tags[createdByCapcTagName] != "" {
150+
return c.DestroyNetwork(csCluster)
151+
}
152+
89153
return nil
90154
}
91155

pkg/cloud/network_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
. "github.com/onsi/ginkgo"
2525
. "github.com/onsi/gomega"
2626
"github.com/pkg/errors"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
2829
)
2930

@@ -36,6 +37,7 @@ var _ = Describe("Network", func() {
3637
fs *cloudstack.MockFirewallServiceIface
3738
as *cloudstack.MockAddressServiceIface
3839
lbs *cloudstack.MockLoadBalancerServiceIface
40+
rs *cloudstack.MockResourcetagsServiceIface
3941
csCluster *infrav1.CloudStackCluster
4042
client cloud.Client
4143
)
@@ -59,6 +61,7 @@ var _ = Describe("Network", func() {
5961
fs = mockClient.Firewall.(*cloudstack.MockFirewallServiceIface)
6062
as = mockClient.Address.(*cloudstack.MockAddressServiceIface)
6163
lbs = mockClient.LoadBalancer.(*cloudstack.MockLoadBalancerServiceIface)
64+
rs = mockClient.Resourcetags.(*cloudstack.MockResourcetagsServiceIface)
6265
client = cloud.NewClientFromCSAPIClient(mockClient)
6366

6467
// Reset csCluster.
@@ -68,13 +71,26 @@ var _ = Describe("Network", func() {
6871
Network: fakeNetName,
6972
ControlPlaneEndpoint: clusterv1.APIEndpoint{Port: int32(6443)},
7073
},
74+
ObjectMeta: metav1.ObjectMeta{
75+
UID: "0",
76+
},
7177
}
7278
})
7379

7480
AfterEach(func() {
7581
mockCtrl.Finish()
7682
})
7783

84+
// Sets expectations network tag creation. To be used by tests that get/create networks.
85+
expectNetworkTags := func(networkId string) {
86+
listTagsParams := &cloudstack.ListTagsParams{}
87+
createTagsParams := &cloudstack.CreateTagsParams{}
88+
rs.EXPECT().NewListTagsParams().Return(listTagsParams)
89+
rs.EXPECT().ListTags(listTagsParams).Return(&cloudstack.ListTagsResponse{}, nil)
90+
rs.EXPECT().NewCreateTagsParams([]string{networkId}, "network", gomock.Any()).Return(createTagsParams)
91+
rs.EXPECT().CreateTags(createTagsParams).Return(&cloudstack.CreateTagsResponse{}, nil)
92+
}
93+
7894
Context("for an existing network", func() {
7995
It("resolves network details in cluster status", func() {
8096
ns.EXPECT().GetNetworkID(fakeNetName).Return(fakeNetId, 1, nil)
@@ -87,13 +103,15 @@ var _ = Describe("Network", func() {
87103
It("does not call to create a new network via GetOrCreateNetwork", func() {
88104
ns.EXPECT().GetNetworkID(fakeNetName).Return(fakeNetId, 1, nil)
89105
ns.EXPECT().GetNetworkByID(fakeNetId).Return(&cloudstack.Network{Type: isolatedNetworkType}, 1, nil)
106+
expectNetworkTags(fakeNetId)
90107

91108
Ω(client.GetOrCreateNetwork(csCluster)).Should(Succeed())
92109
})
93110

94111
It("resolves network details with network ID instead of network name", func() {
95112
ns.EXPECT().GetNetworkID(gomock.Any()).Return("", -1, errors.New("No match found for blah."))
96113
ns.EXPECT().GetNetworkByID(fakeNetId).Return(&cloudstack.Network{Type: isolatedNetworkType}, 1, nil)
114+
expectNetworkTags(fakeNetId)
97115

98116
csCluster.Spec.Network = fakeNetId
99117
Ω(client.GetOrCreateNetwork(csCluster)).Should(Succeed())
@@ -108,6 +126,8 @@ var _ = Describe("Network", func() {
108126
ns.EXPECT().NewCreateNetworkParams(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
109127
Return(&cloudstack.CreateNetworkParams{})
110128
ns.EXPECT().CreateNetwork(gomock.Any()).Return(&cloudstack.CreateNetworkResponse{Id: netId}, nil)
129+
expectNetworkTags(netId)
130+
111131
Ω(client.GetOrCreateNetwork(csCluster)).Should(Succeed())
112132
})
113133
})

0 commit comments

Comments
 (0)