Skip to content

Commit a52f03a

Browse files
authored
feat: add tags when updating a security group (#101)
* feat: add tags when updating a security group * refactor: function signature * chore: apply pr feedback
1 parent 02e152e commit a52f03a

File tree

3 files changed

+159
-5
lines changed

3 files changed

+159
-5
lines changed

neutron/live_test.go

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func (s *LiveTests) TestSecurityGroupsV2(c *gc.C) {
218218
c.Errorf("expected to find added security group %s", newSecGrp)
219219
}
220220
// Change the created SecurityGroup's name
221-
updatedSecGroup, err := s.neutron.UpdateSecurityGroupV2(newSecGrp.Id, "NameChanged", "")
221+
updatedSecGroup, err := s.neutron.UpdateSecurityGroupV2(newSecGrp.Id, "NameChanged", "", []string{})
222222
c.Assert(err, gc.IsNil)
223223
// Verify the name change
224224
foundSecGrps, err := s.neutron.SecurityGroupByNameV2(updatedSecGroup.Name)
@@ -238,6 +238,96 @@ func (s *LiveTests) TestSecurityGroupsV2(c *gc.C) {
238238
c.Assert(err, gc.Not(gc.IsNil))
239239
}
240240

241+
func (s *LiveTests) TestUpdateSecurityGroupsWithTagsV2(c *gc.C) {
242+
newSecGrp, err := s.neutron.CreateSecurityGroupV2("SecurityGroupTest", "Testing create security group", []string{})
243+
c.Assert(err, gc.IsNil)
244+
c.Assert(newSecGrp, gc.Not(gc.IsNil))
245+
c.Assert(newSecGrp.Tags, gc.HasLen, 0)
246+
defer s.deleteSecurityGroup(newSecGrp.Id, c)
247+
query := neutron.ListSecurityGroupsV2Query{}
248+
secGrps, err := s.neutron.ListSecurityGroupsV2(query)
249+
c.Assert(err, gc.IsNil)
250+
c.Assert(secGrps, gc.Not(gc.HasLen), 0)
251+
var found bool
252+
for _, secGrp := range secGrps {
253+
c.Check(secGrp.Id, gc.Not(gc.Equals), "")
254+
c.Check(secGrp.Name, gc.Not(gc.Equals), "")
255+
c.Check(secGrp.Description, gc.Not(gc.Equals), "")
256+
c.Check(secGrp.TenantId, gc.Not(gc.Equals), "")
257+
// Is this the SecurityGroup we just created?
258+
if secGrp.Id == newSecGrp.Id {
259+
found = true
260+
}
261+
}
262+
if !found {
263+
c.Errorf("expected to find added security group %s", newSecGrp)
264+
}
265+
// Change the created SecurityGroup's name
266+
updatedSecGroup, err := s.neutron.UpdateSecurityGroupV2(newSecGrp.Id, "NameChanged", "", []string{"new-tag-here"})
267+
c.Assert(err, gc.IsNil)
268+
// Verify the name change
269+
foundSecGrps, err := s.neutron.SecurityGroupByNameV2(updatedSecGroup.Name)
270+
c.Assert(err, gc.IsNil)
271+
c.Assert(foundSecGrps, gc.Not(gc.HasLen), 0)
272+
c.Assert(updatedSecGroup.Tags, gc.HasLen, 1)
273+
c.Assert(updatedSecGroup.Tags[0], gc.Equals, "new-tag-here")
274+
found = false
275+
for _, secGrp := range foundSecGrps {
276+
if secGrp.Id == updatedSecGroup.Id {
277+
found = true
278+
break
279+
}
280+
}
281+
if !found {
282+
c.Errorf("expected to find added security group %s, when requested by name", updatedSecGroup.Name)
283+
}
284+
_, err = s.neutron.SecurityGroupByNameV2(newSecGrp.Name)
285+
c.Assert(err, gc.Not(gc.IsNil))
286+
}
287+
288+
func (s *LiveTests) TestUpdateSecurityGroupsWithTagsV2Rollback(c *gc.C) {
289+
newSecGrp, err := s.neutron.CreateSecurityGroupV2("SecurityGroupTest", "Testing create security group", []string{"awesome-group"})
290+
c.Assert(err, gc.IsNil)
291+
c.Assert(newSecGrp, gc.Not(gc.IsNil))
292+
c.Assert(newSecGrp.Tags, gc.HasLen, 1)
293+
defer s.deleteSecurityGroup(newSecGrp.Id, c)
294+
query := neutron.ListSecurityGroupsV2Query{
295+
Tags: []string{"awesome-group"},
296+
}
297+
secGrps, err := s.neutron.ListSecurityGroupsV2(query)
298+
c.Assert(err, gc.IsNil)
299+
c.Assert(secGrps, gc.Not(gc.HasLen), 0)
300+
var found bool
301+
for _, secGrp := range secGrps {
302+
c.Check(secGrp.Id, gc.Not(gc.Equals), "")
303+
c.Check(secGrp.Name, gc.Not(gc.Equals), "")
304+
c.Check(secGrp.Description, gc.Not(gc.Equals), "")
305+
c.Check(secGrp.TenantId, gc.Not(gc.Equals), "")
306+
// Is this the SecurityGroup we just created?
307+
if secGrp.Id == newSecGrp.Id {
308+
found = true
309+
}
310+
}
311+
if !found {
312+
c.Errorf("expected to find added security group %s", newSecGrp)
313+
}
314+
// The given tag forces it to rollback
315+
updatedSecGroup, err := s.neutron.UpdateSecurityGroupV2(newSecGrp.Id, "NameChanged", "", []string{"unit-test-rollback"})
316+
c.Assert(updatedSecGroup, gc.IsNil)
317+
c.Assert(err, gc.NotNil)
318+
c.Assert(strings.Contains(err.Error(), "updating tags failed, rolled back security group"), gc.Equals, true)
319+
// Verify that the name is still the same
320+
foundSecGrps, err := s.neutron.SecurityGroupByNameV2(newSecGrp.Name)
321+
c.Assert(foundSecGrps, gc.HasLen, 1)
322+
c.Assert(err, gc.IsNil)
323+
c.Assert(foundSecGrps, gc.NotNil)
324+
c.Assert(foundSecGrps[0].Name, gc.Equals, "SecurityGroupTest")
325+
c.Assert(foundSecGrps[0].Tags, gc.HasLen, 1)
326+
327+
nonExistingGroups, _ := s.neutron.SecurityGroupByNameV2("NameChanged")
328+
c.Assert(nonExistingGroups, gc.HasLen, 0)
329+
}
330+
241331
func (s *LiveTests) TestSecurityGroupsV2WithTags(c *gc.C) {
242332
newSecGrp, err := s.neutron.CreateSecurityGroupV2("SecurityGroupTest", "Testing create security group", []string{"awesome-group"})
243333
c.Assert(err, gc.IsNil)

neutron/neutron.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,8 +466,23 @@ func (c *Client) DeleteSecurityGroupV2(groupId string) error {
466466
return err
467467
}
468468

469+
// GetSecurityGroupV2 finds a security group by ID.
470+
func (c *Client) GetSecurityGroupV2(groupId string) (*SecurityGroupV2, error) {
471+
var resp struct {
472+
SecurityGroup SecurityGroupV2 `json:"security_group"`
473+
}
474+
url := fmt.Sprintf("%s/%s", ApiSecurityGroupsV2, groupId)
475+
requestData := goosehttp.RequestData{ExpectedStatus: []int{http.StatusOK}, RespValue: &resp}
476+
err := c.client.SendRequest(client.GET, "network", "v2.0", url, &requestData)
477+
if err != nil {
478+
return nil, errors.Newf(err, "failed to show security group with ID: %s", groupId)
479+
}
480+
481+
return &resp.SecurityGroup, nil
482+
}
483+
469484
// UpdateSecurityGroupV2 updates the name and description of the given group.
470-
func (c *Client) UpdateSecurityGroupV2(groupId, name, description string) (*SecurityGroupV2, error) {
485+
func (c *Client) updateSecurityGroupV2(groupId, name, description string) (*SecurityGroupV2, error) {
471486
var req struct {
472487
SecurityGroupV2 struct {
473488
Name string `json:"name"`
@@ -488,6 +503,41 @@ func (c *Client) UpdateSecurityGroupV2(groupId, name, description string) (*Secu
488503
return &resp.SecurityGroup, nil
489504
}
490505

506+
// UpdateSecurityGroupV2 updates the name, description, and tags (if specified) of the given group.
507+
// When tags are passed, it will overwrite the existing tags that are attached to the group.
508+
func (c *Client) UpdateSecurityGroupV2(groupId, name, description string, tags []string) (*SecurityGroupV2, error) {
509+
existingGroup, err := c.GetSecurityGroupV2(groupId)
510+
if err != nil {
511+
return nil, errors.Newf(err, "fetching an existing security group failed with id: %s", groupId)
512+
}
513+
oldName := existingGroup.Name
514+
oldDescription := existingGroup.Description
515+
516+
securityGroup, err := c.updateSecurityGroupV2(groupId, name, description)
517+
if err != nil {
518+
return nil, errors.Newf(err, "updating a security group failed with id: %s", groupId)
519+
}
520+
521+
if len(tags) == 0 {
522+
return securityGroup, nil
523+
}
524+
525+
// Updates the tags for the group.
526+
err = c.ReplaceAllTags("security-groups", securityGroup.Id, tags)
527+
if err != nil {
528+
// Rollback to old name and description if updating tags failed.
529+
if _, updateErr := c.updateSecurityGroupV2(groupId, oldName, oldDescription); updateErr != nil {
530+
return nil, errors.Newf(updateErr, "updating tags and attempt to roll back failed for security group with id: %s", securityGroup.Id)
531+
}
532+
533+
return nil, errors.Newf(err, "updating tags failed, rolled back security group with id: %s", securityGroup.Id)
534+
}
535+
536+
securityGroup.Tags = tags
537+
538+
return securityGroup, nil
539+
}
540+
491541
// RuleInfoV2 allows the callers of CreateSecurityGroupRuleV2() to
492542
// create 2 types of security group rules: ingress rules and egress
493543
// rules. Security Groups are applied on neutron ports.

testservices/neutronmodel/neutronmodel.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,22 @@ func (n *NeutronModel) UpdateSecurityGroup(group neutron.SecurityGroupV2) error
177177
}
178178
existingGroup.Name = group.Name
179179
existingGroup.Description = group.Description
180-
existingGroup.Tags = group.Tags
180+
n.groups[group.Id] = *existingGroup
181+
return nil
182+
}
183+
184+
// UpdateSecurityGroupWithTags updates an existing security group with tags given a
185+
// neutron.SecurityGroupRuleV2.
186+
func (n *NeutronModel) UpdateSecurityGroupWithTags(group neutron.SecurityGroupV2, tags []string) error {
187+
n.rwMu.Lock()
188+
defer n.rwMu.Unlock()
189+
existingGroup, err := n.SecurityGroup(group.Id)
190+
if err != nil {
191+
return testservices.NewSecurityGroupByIDNotFoundError(group.Id)
192+
}
193+
existingGroup.Name = group.Name
194+
existingGroup.Description = group.Description
195+
existingGroup.Tags = tags
181196
n.groups[group.Id] = *existingGroup
182197
return nil
183198
}
@@ -232,8 +247,7 @@ func (n *NeutronModel) AddTagsToSecurityGroup(groupId string, tags []string) err
232247
if group == nil {
233248
return testservices.NewNotFoundError(fmt.Sprintf("Resource security_groups %s could not be found.", groupId))
234249
}
235-
group.Tags = tags
236-
return n.UpdateSecurityGroup(*group)
250+
return n.UpdateSecurityGroupWithTags(*group, tags)
237251
}
238252

239253
// AddNovaSecurityGroup creates a new security group given a nova.SecurityGroup.

0 commit comments

Comments
 (0)