Skip to content

Commit 1f60541

Browse files
committed
Initial work for tagging and cleaning up IP addresses
1 parent 0112105 commit 1f60541

File tree

6 files changed

+123
-76
lines changed

6 files changed

+123
-76
lines changed

config/webhook/manifests.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
---
23
apiVersion: admissionregistration.k8s.io/v1
34
kind: MutatingWebhookConfiguration
@@ -45,6 +46,7 @@ webhooks:
4546
resources:
4647
- cloudstackmachines
4748
sideEffects: None
49+
4850
---
4951
apiVersion: admissionregistration.k8s.io/v1
5052
kind: ValidatingWebhookConfiguration

pkg/cloud/affinity_groups.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type AffinityGroupIFace interface {
3737
GetOrCreateAffinityGroup(*infrav1.CloudStackCluster, *AffinityGroup) error
3838
DeleteAffinityGroup(*AffinityGroup) error
3939
AssociateAffinityGroup(*infrav1.CloudStackMachine, AffinityGroup) error
40-
DissassociateAffinityGroup(*infrav1.CloudStackMachine, AffinityGroup) error
40+
DisassociateAffinityGroup(*infrav1.CloudStackMachine, AffinityGroup) error
4141
}
4242

4343
func (c *client) FetchAffinityGroup(group *AffinityGroup) (reterr error) {
@@ -171,7 +171,7 @@ func (c *client) AssociateAffinityGroup(csMachine *infrav1.CloudStackMachine, gr
171171
}
172172
}
173173

174-
func (c *client) DissassociateAffinityGroup(csMachine *infrav1.CloudStackMachine, group AffinityGroup) (retErr error) {
174+
func (c *client) DisassociateAffinityGroup(csMachine *infrav1.CloudStackMachine, group AffinityGroup) (retErr error) {
175175
if groups, err := c.GetCurrentAffinityGroups(csMachine); err != nil {
176176
return err
177177
} else {

pkg/cloud/cluster.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,18 @@ func (c *client) GetOrCreateCluster(csCluster *infrav1.CloudStackCluster) (retEr
9595
}
9696

9797
func (c *client) DisposeClusterResources(csCluster *infrav1.CloudStackCluster) (retError error) {
98-
if err := c.RemoveClusterTagFromNetwork(csCluster); err != nil {
98+
if csCluster.Status.PublicIPID != "" {
99+
if err := c.DeleteClusterTag(ResourceTypeIpAddress, csCluster.Status.PublicIPID, csCluster); err != nil {
100+
return err
101+
}
102+
if err := c.DisassociatePublicIpAddressIfNotInUse(csCluster); err != nil {
103+
return err
104+
}
105+
}
106+
107+
if err := c.DeleteClusterTag(ResourceTypeNetwork, csCluster.Status.NetworkID, csCluster); err != nil {
99108
return err
100109
}
110+
101111
return c.DeleteNetworkIfNotInUse(csCluster)
102112
}

pkg/cloud/network.go

Lines changed: 19 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,9 @@ 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-
6561
func (c *client) GetOrCreateNetwork(csCluster *infrav1.CloudStackCluster) (retErr error) {
6662
if retErr = c.ResolveNetwork(csCluster); retErr == nil { // Found network.
67-
return addClusterTags(c, csCluster, false)
63+
return c.AddClusterTag(ResourceTypeNetwork, csCluster.Status.NetworkID, csCluster, false)
6864
} else if !strings.Contains(retErr.Error(), "No match found") { // Some other error.
6965
return retErr
7066
} // Network not found.
@@ -90,63 +86,29 @@ func (c *client) GetOrCreateNetwork(csCluster *infrav1.CloudStackCluster) (retEr
9086
csCluster.Status.NetworkID = resp.Id
9187
csCluster.Status.NetworkType = resp.Type
9288

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
89+
return c.AddClusterTag(ResourceTypeNetwork, csCluster.Status.NetworkID, csCluster, true)
11890
}
11991

120-
func (c *client) RemoveClusterTagFromNetwork(csCluster *infrav1.CloudStackCluster) (retError error) {
121-
tags, err := c.GetNetworkTags(csCluster.Status.NetworkID)
92+
func (c *client) DisassociatePublicIpAddressIfNotInUse(csCluster *infrav1.CloudStackCluster) (retError error) {
93+
okayToDelete, err := c.DoClusterTagsAllowDisposal(ResourceTypeIpAddress, csCluster.Status.PublicIPID)
12294
if err != nil {
12395
return err
12496
}
12597

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-
}
98+
if okayToDelete {
99+
return c.DisassociatePublicIpAddress(csCluster)
131100
}
132101

133102
return nil
134103
}
135104

136105
func (c *client) DeleteNetworkIfNotInUse(csCluster *infrav1.CloudStackCluster) (retError error) {
137-
tags, err := c.GetNetworkTags(csCluster.Status.NetworkID)
106+
okayToDelete, err := c.DoClusterTagsAllowDisposal(ResourceTypeNetwork, csCluster.Status.NetworkID)
138107
if err != nil {
139108
return err
140109
}
141110

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] != "" {
111+
if okayToDelete {
150112
return c.DestroyNetwork(csCluster)
151113
}
152114

@@ -171,12 +133,12 @@ func (c *client) ResolvePublicIPDetails(csCluster *infrav1.CloudStackCluster) (*
171133
// Ignore already allocated here since the IP was specified.
172134
return publicAddresses.PublicIpAddresses[0], nil
173135
} else if publicAddresses.Count > 0 { // Endpoint not specified.
174-
for _, v := range publicAddresses.PublicIpAddresses { // Pick first availabe address.
136+
for _, v := range publicAddresses.PublicIpAddresses { // Pick first available address.
175137
if v.Allocated == "" { // Found un-allocated Public IP.
176138
return v, nil
177139
}
178140
}
179-
return nil, errors.New("all Public IP Adresse(s) found were already allocated")
141+
return nil, errors.New("all Public IP Address(es) found were already allocated")
180142
}
181143
return nil, errors.Errorf(`no public addresses found in network: "%s"`, csCluster.Spec.Network)
182144
}
@@ -190,10 +152,11 @@ func (c *client) AssociatePublicIpAddress(csCluster *infrav1.CloudStackCluster)
190152

191153
csCluster.Spec.ControlPlaneEndpoint.Host = publicAddress.Ipaddress
192154
csCluster.Status.PublicIPID = publicAddress.Id
155+
allocatedByCapc := publicAddress.Allocated == ""
193156

194157
if publicAddress.Allocated != "" && publicAddress.Associatednetworkid == csCluster.Status.NetworkID {
195158
// Address already allocated to network. Allocated is a timestamp -- not a boolean.
196-
return nil
159+
return c.AddClusterTag(ResourceTypeIpAddress, publicAddress.Id, csCluster, allocatedByCapc)
197160
} // Address not yet allocated. Allocate now.
198161

199162
// Public IP found, but not yet allocated to network.
@@ -205,7 +168,13 @@ func (c *client) AssociatePublicIpAddress(csCluster *infrav1.CloudStackCluster)
205168
if _, err := c.cs.Address.AssociateIpAddress(p); err != nil {
206169
return err
207170
}
208-
return nil
171+
return c.AddClusterTag(ResourceTypeIpAddress, publicAddress.Id, csCluster, allocatedByCapc)
172+
}
173+
174+
func (c *client) DisassociatePublicIpAddress(csCluster *infrav1.CloudStackCluster) (retErr error) {
175+
p := c.cs.Address.NewDisassociateIpAddressParams(csCluster.Status.PublicIPID)
176+
_, retErr = c.cs.Address.DisassociateIpAddress(p)
177+
return retErr
209178
}
210179

211180
func (c *client) OpenFirewallRules(csCluster *infrav1.CloudStackCluster) (retErr error) {

pkg/cloud/tags.go

Lines changed: 83 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2022.
2+
Copyright 2022 The Kubernetes Authors.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -16,30 +16,93 @@ limitations under the License.
1616

1717
package cloud
1818

19+
import (
20+
infrav1 "github.com/aws/cluster-api-provider-cloudstack/api/v1beta1"
21+
"strings"
22+
)
23+
1924
type TagIFace interface {
20-
AddNetworkTags(string, map[string]string) error
21-
GetNetworkTags(string) (map[string]string, error)
22-
DeleteNetworkTags(string, map[string]string) error
25+
AddClusterTag(resourceType ResourceType, resourceId string, csCluster *infrav1.CloudStackCluster, addCreatedByCapcTag bool) error
26+
DeleteClusterTag(resourceType ResourceType, resourceId string, csCluster *infrav1.CloudStackCluster) error
27+
DoClusterTagsAllowDisposal(resourceType ResourceType, resourceId string) (bool, error)
28+
AddTags(resourceType ResourceType, resourceId string, tags map[string]string) error
29+
GetTags(resourceType ResourceType, resourceId string) (map[string]string, error)
30+
DeleteTags(resourceType ResourceType, resourceId string, tagsToDelete map[string]string) error
2331
}
2432

33+
type ResourceType string
34+
2535
const (
26-
clusterTagNamePrefix = "CAPC_cluster_"
27-
createdByCapcTagName = "created_by_CAPC"
28-
resourceTypeNetwork = "network"
36+
clusterTagNamePrefix = "CAPC_cluster_"
37+
createdByCapcTagName = "created_by_CAPC"
38+
ResourceTypeNetwork ResourceType = "network"
39+
ResourceTypeIpAddress ResourceType = "ipaddress"
2940
)
3041

31-
// TagNetwork adds tags to a network by network id.
32-
func (c *client) AddNetworkTags(networkId string, tags map[string]string) error {
33-
p := c.cs.Resourcetags.NewCreateTagsParams([]string{networkId}, resourceTypeNetwork, tags)
42+
func (c *client) AddClusterTag(resourceType ResourceType, resourceId string, csCluster *infrav1.CloudStackCluster, addCreatedByCapcTag bool) error {
43+
clusterTagName := generateClusterTagName(csCluster)
44+
newTags := map[string]string{}
45+
46+
existingTags, err := c.GetTags(resourceType, resourceId)
47+
if err != nil {
48+
return err
49+
}
50+
51+
if existingTags[clusterTagName] == "" {
52+
newTags[clusterTagName] = "1"
53+
}
54+
55+
if addCreatedByCapcTag && existingTags[createdByCapcTagName] == "" {
56+
newTags[createdByCapcTagName] = "1"
57+
}
58+
59+
if len(newTags) > 0 {
60+
return c.AddTags(resourceType, resourceId, newTags)
61+
}
62+
63+
return nil
64+
}
65+
66+
func (c *client) DeleteClusterTag(resourceType ResourceType, resourceId string, csCluster *infrav1.CloudStackCluster) error {
67+
tags, err := c.GetTags(resourceType, csCluster.Status.NetworkID)
68+
if err != nil {
69+
return err
70+
}
71+
72+
clusterTagName := generateClusterTagName(csCluster)
73+
if tagValue := tags[clusterTagName]; tagValue != "" {
74+
return c.DeleteTags(resourceType, csCluster.Status.NetworkID, map[string]string{clusterTagName: tagValue})
75+
}
76+
77+
return nil
78+
}
79+
80+
func (c *client) DoClusterTagsAllowDisposal(resourceType ResourceType, resourceId string) (bool, error) {
81+
tags, err := c.GetTags(resourceType, resourceId)
82+
if err != nil {
83+
return false, err
84+
}
85+
86+
var clusterTagCount int
87+
for tagName := range tags {
88+
if strings.HasPrefix(tagName, clusterTagNamePrefix) {
89+
clusterTagCount++
90+
}
91+
}
92+
93+
return clusterTagCount == 0 && tags[createdByCapcTagName] != "", nil
94+
}
95+
96+
func (c *client) AddTags(resourceType ResourceType, resourceId string, tags map[string]string) error {
97+
p := c.cs.Resourcetags.NewCreateTagsParams([]string{resourceId}, string(resourceType), tags)
3498
_, err := c.cs.Resourcetags.CreateTags(p)
3599
return err
36100
}
37101

38-
// GetNetworkTags gets tags by network id.
39-
func (c *client) GetNetworkTags(networkId string) (map[string]string, error) {
102+
func (c *client) GetTags(resourceType ResourceType, resourceId string) (map[string]string, error) {
40103
p := c.cs.Resourcetags.NewListTagsParams()
41-
p.SetResourceid(networkId)
42-
p.SetResourcetype(resourceTypeNetwork)
104+
p.SetResourceid(resourceId)
105+
p.SetResourcetype(string(resourceType))
43106
if listTagResponse, err := c.cs.Resourcetags.ListTags(p); err != nil {
44107
return nil, err
45108
} else {
@@ -51,10 +114,13 @@ func (c *client) GetNetworkTags(networkId string) (map[string]string, error) {
51114
}
52115
}
53116

54-
// DeleteNetworkTags deletes matching tags from a network
55-
func (c *client) DeleteNetworkTags(networkId string, tagsToDelete map[string]string) error {
56-
p := c.cs.Resourcetags.NewDeleteTagsParams([]string{networkId}, resourceTypeNetwork)
117+
func (c *client) DeleteTags(resourceType ResourceType, resourceId string, tagsToDelete map[string]string) error {
118+
p := c.cs.Resourcetags.NewDeleteTagsParams([]string{resourceId}, string(resourceType))
57119
p.SetTags(tagsToDelete)
58120
_, err := c.cs.Resourcetags.DeleteTags(p)
59121
return err
60122
}
123+
124+
func generateClusterTagName(csCluster *infrav1.CloudStackCluster) string {
125+
return clusterTagNamePrefix + string(csCluster.UID)
126+
}

pkg/cloud/tags_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2022.
2+
Copyright 2022 The Kubernetes Authors.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -68,19 +68,19 @@ var _ = Describe("Tag Unit Tests", func() {
6868

6969
It("Tags a network with an arbitrary tag.", func() {
7070
// Delete the tag if it already exists from a prior test run, otherwise the test will fail.
71-
_ = client.DeleteNetworkTags(networkId, testTags)
72-
Ω(client.AddNetworkTags(networkId, testTags)).Should(Succeed())
71+
_ = client.DeleteTags(cloud.ResourceTypeNetwork, networkId, testTags)
72+
Ω(client.AddTags(cloud.ResourceTypeNetwork, networkId, testTags)).Should(Succeed())
7373
})
7474

7575
It("Fetches said tag.", func() {
76-
tags, err := client.GetNetworkTags(networkId)
76+
tags, err := client.GetTags(cloud.ResourceTypeNetwork, networkId)
7777
Ω(err).Should(BeNil())
7878
Ω(tags[tagKey]).Should(Equal(tagValue))
7979
})
8080

8181
It("Deletes said tag.", func() {
82-
Ω(client.DeleteNetworkTags(networkId, testTags)).Should(Succeed())
83-
remainingTags, err := client.GetNetworkTags(networkId)
82+
Ω(client.DeleteTags(cloud.ResourceTypeNetwork, networkId, testTags)).Should(Succeed())
83+
remainingTags, err := client.GetTags(cloud.ResourceTypeNetwork, networkId)
8484
Ω(err).Should(BeNil())
8585
Ω(remainingTags[tagKey]).Should(Equal(""))
8686
})

0 commit comments

Comments
 (0)