Skip to content

Commit 5a9ef7c

Browse files
authored
PCP-5371: fixed network tags to always have one capc cluster tag at a time (#7)
1 parent 1145aa8 commit 5a9ef7c

File tree

4 files changed

+54
-1
lines changed

4 files changed

+54
-1
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,5 @@ cloud-config.yaml
4949
*~
5050
examples/*
5151
hack/tools/share/*
52+
53+
vendor/

pkg/cloud/isolated_network_test.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,19 @@ var _ = ginkgo.Describe("Network", func() {
105105
Return(&csapi.CreateEgressFirewallRuleResponse{}, nil))
106106

107107
// Will add cluster tag once to Network and once to PublicIP.
108+
// Each AddClusterTag calls GetTags twice (IsCapcManaged + removeOldClusterTags)
108109
createdByResponse := &csapi.ListTagsResponse{Tags: []*csapi.Tag{{Key: cloud.CreatedByCAPCTagName, Value: "1"}}}
109110
gomock.InOrder(
111+
// Network: IsCapcManaged GetTags
110112
rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}),
111113
rs.EXPECT().ListTags(gomock.Any()).Return(createdByResponse, nil),
114+
// Network: removeOldClusterTags GetTags
115+
rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}),
116+
rs.EXPECT().ListTags(gomock.Any()).Return(createdByResponse, nil),
117+
// PublicIP: IsCapcManaged GetTags
118+
rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}),
119+
rs.EXPECT().ListTags(gomock.Any()).Return(createdByResponse, nil),
120+
// PublicIP: removeOldClusterTags GetTags
112121
rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}),
113122
rs.EXPECT().ListTags(gomock.Any()).Return(createdByResponse, nil))
114123

@@ -252,9 +261,14 @@ var _ = ginkgo.Describe("Network", func() {
252261
aip := &csapi.AssociateIpAddressParams{}
253262
as.EXPECT().NewAssociateIpAddressParams().Return(aip)
254263
as.EXPECT().AssociateIpAddress(aip).Return(&csapi.AssociateIpAddressResponse{}, nil)
255-
// Will add cluster tag once to Network and once to PublicIP.
264+
// Will add cluster tag once to PublicIP
265+
// Each AddClusterTag calls GetTags twice (IsCapcManaged + removeOldClusterTags)
256266
createdByResponse := &csapi.ListTagsResponse{Tags: []*csapi.Tag{{Key: cloud.CreatedByCAPCTagName, Value: "1"}}}
257267
gomock.InOrder(
268+
// PublicIP: IsCapcManaged GetTags
269+
rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}),
270+
rs.EXPECT().ListTags(gomock.Any()).Return(createdByResponse, nil),
271+
// PublicIP: removeOldClusterTags GetTags
258272
rs.EXPECT().NewListTagsParams().Return(&csapi.ListTagsParams{}),
259273
rs.EXPECT().ListTags(gomock.Any()).Return(createdByResponse, nil))
260274

pkg/cloud/tags.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,18 @@ func (c *client) IsCapcManaged(resourceType ResourceType, resourceID string) (bo
6565
}
6666

6767
// AddClusterTag adds cluster tag to a resource. This tag indicates the resource is used by a given the cluster.
68+
// If old cluster tags exist (from previous cluster UIDs), they are removed first to prevent dual-tagging.
6869
func (c *client) AddClusterTag(rType ResourceType, rID string, csCluster *infrav1.CloudStackCluster) error {
6970
if managedByCAPC, err := c.IsCapcManaged(rType, rID); err != nil {
7071
return err
7172
} else if managedByCAPC {
7273
ClusterTagName := generateClusterTagName(csCluster)
74+
75+
// Remove old cluster tags before adding new one to prevent dual-tagging during pivot
76+
if err := c.removeOldClusterTags(rType, rID, ClusterTagName); err != nil {
77+
return errors.Wrapf(err, "removing old cluster tags from %s %s", rType, rID)
78+
}
79+
7380
return c.AddTags(rType, rID, map[string]string{ClusterTagName: "1"})
7481
}
7582
return nil
@@ -163,6 +170,32 @@ func (c *client) DeleteTags(resourceType ResourceType, resourceID string, tagsTo
163170
return nil
164171
}
165172

173+
// removeOldClusterTags removes any cluster tags that don't match the current cluster UID.
174+
// This prevents dual-tagging when a resource is moved during pivot and gets a new cluster UID.
175+
func (c *client) removeOldClusterTags(rType ResourceType, rID string, currentClusterTagName string) error {
176+
tags, err := c.GetTags(rType, rID)
177+
if err != nil {
178+
return errors.Wrapf(err, "getting tags for %s %s", rType, rID)
179+
}
180+
181+
oldClusterTags := make(map[string]string)
182+
for tagName, tagValue := range tags {
183+
// Find cluster tags that are NOT the current cluster's tag
184+
if strings.HasPrefix(tagName, ClusterTagNamePrefix) && tagName != currentClusterTagName {
185+
oldClusterTags[tagName] = tagValue
186+
}
187+
}
188+
189+
// Delete old cluster tags if any exist
190+
if len(oldClusterTags) > 0 {
191+
if err := c.DeleteTags(rType, rID, oldClusterTags); err != nil {
192+
return errors.Wrapf(err, "deleting old cluster tags %v from %s %s", oldClusterTags, rType, rID)
193+
}
194+
}
195+
196+
return nil
197+
}
198+
166199
func generateClusterTagName(csCluster *infrav1.CloudStackCluster) string {
167200
return ClusterTagNamePrefix + string(csCluster.UID)
168201
}

pkg/cloud/tags_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ var _ = ginkgo.Describe("Tag Unit Tests", func() {
156156
createdByCAPCResponse := &csapi.ListTagsResponse{Tags: []*csapi.Tag{{Key: cloud.CreatedByCAPCTagName, Value: "1"}}}
157157
rtlp := &csapi.ListTagsParams{}
158158
ctp := &csapi.CreateTagsParams{}
159+
// First GetTags call in IsCapcManaged
160+
rs.EXPECT().NewListTagsParams().Return(rtlp)
161+
rs.EXPECT().ListTags(rtlp).Return(createdByCAPCResponse, nil)
162+
// Second GetTags call in removeOldClusterTags
159163
rs.EXPECT().NewListTagsParams().Return(rtlp)
160164
rs.EXPECT().ListTags(rtlp).Return(createdByCAPCResponse, nil)
161165
rs.EXPECT().NewCreateTagsParams(gomock.Any(), gomock.Any(), gomock.Any()).Return(ctp)

0 commit comments

Comments
 (0)