Merge lp:~adeuring/juju-core/1227574 into lp:~go-bot/juju-core/trunk

Proposed by Abel Deuring
Status: Merged
Approved by: Abel Deuring
Approved revision: no longer in the source branch.
Merged at revision: 2586
Proposed branch: lp:~adeuring/juju-core/1227574
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 219 lines (+167/-2)
2 files modified
provider/openstack/local_test.go (+105/-0)
provider/openstack/provider.go (+62/-2)
To merge this branch: bzr merge lp:~adeuring/juju-core/1227574
Reviewer Review Type Date Requested Status
Abel Deuring (community) Approve
Review via email: mp+214520@code.launchpad.net

Commit message

[r=TheMue][bug=1227574] Remove OpenStack security groups a machine is removed or the environment is destroyed.

Description of the change

[r=TheMue] destroy-env should remove security groups

fix for bug 1227574.

The implementation is obvious, I think. As described in bug 1300755
it can happen that a security group cannot be deleted because it is
used in another environment. The Nova API call fails in this case,
so trying this call does not affect the other environment.

Unfortunately, goose hides the original error message, so Destroy()
ans StopInstance() cannot determine the exact reason of the failure.
Hence only a warning is logged.

https://codereview.appspot.com/84470053/

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

Reviewers: mp+214520_code.launchpad.net,

Message:
Please take a look.

Description:
destroy-env should remove security groups

fix for bug 1227574.

The implementation is obvious, I think. As described in bug 1300755
it can happen that a security group cannot be deleted because it is
used in another environment. The Nova API call fails in this case,
so trying this call does not affect the other environment.

Unfortunately, goose hides the original error message, so Destroy()
ans StopInstance() cannot determine the exact reason of the failure.
Hence only a warning is logged.

https://code.launchpad.net/~adeuring/juju-core/1227574/+merge/214520

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/84470053/

Affected files (+168, -2 lines):
   A [revision details]
   M provider/openstack/local_test.go
   M provider/openstack/provider.go

Revision history for this message
Frank Mueller (themue) wrote :

Only minor notes, otherwise LGTM.

https://codereview.appspot.com/84470053/diff/1/provider/openstack/local_test.go
File provider/openstack/local_test.go (right):

https://codereview.appspot.com/84470053/diff/1/provider/openstack/local_test.go#newcode452
provider/openstack/local_test.go:452: func (s *localServerSuite)
TestDestroyEnvironmentDeletesSecurityGroupsFWModeGlobabel(c *gc.C) {
Hehe, only naming: ...FWModeGlobal. ;)

https://codereview.appspot.com/84470053/diff/1/provider/openstack/provider.go
File provider/openstack/provider.go (right):

https://codereview.appspot.com/84470053/diff/1/provider/openstack/provider.go#newcode879
provider/openstack/provider.go:879: lastDash :=
strings.LastIndex(openstackName, "-")
Would call it "lastDashPos".

https://codereview.appspot.com/84470053/diff/1/provider/openstack/provider.go#newcode881
provider/openstack/provider.go:881: return fmt.Errorf("Cannot identify
instance ID in openstack server name %q", openstackName)
IMHO errors start lower-case.

https://codereview.appspot.com/84470053/diff/1/provider/openstack/provider.go#newcode1001
provider/openstack/provider.go:1001: logger.Warningf("Cannot delete
security group %q. Used by another environment?", group.Name)
Same here, lower-case.

https://codereview.appspot.com/84470053/

Revision history for this message
Abel Deuring (adeuring) :
review: Approve
Revision history for this message
Go Bot (go-bot) wrote :

The attempt to merge lp:~adeuring/juju-core/1227574 into lp:juju-core failed. Below is the output from the failed tests.

# launchpad.net/juju-core/provider/openstack
provider/openstack/provider.go:862: undefined: strings
mongod: no process found

Revision history for this message
Abel Deuring (adeuring) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'provider/openstack/local_test.go'
2--- provider/openstack/local_test.go 2014-04-04 15:55:19 +0000
3+++ provider/openstack/local_test.go 2014-04-08 15:52:29 +0000
4@@ -321,6 +321,111 @@
5 "404; error info: .*itemNotFound.*")
6 }
7
8+func assertSecurityGroups(c *gc.C, env environs.Environ, expected []string) {
9+ novaClient := openstack.GetNovaClient(env)
10+ groups, err := novaClient.ListSecurityGroups()
11+ c.Assert(err, gc.IsNil)
12+ for _, name := range expected {
13+ found := false
14+ for _, group := range groups {
15+ if group.Name == name {
16+ found = true
17+ break
18+ }
19+ }
20+ if !found {
21+ c.Errorf("expected security group %g not found", name)
22+ }
23+ }
24+ for _, group := range groups {
25+ found := false
26+ for _, name := range expected {
27+ if group.Name == name {
28+ found = true
29+ break
30+ }
31+ }
32+ if !found {
33+ c.Errorf("existing security group %g is not expected", group.Name)
34+ }
35+ }
36+}
37+
38+func (s *localServerSuite) TestStopInstance(c *gc.C) {
39+ cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
40+ "firewall-mode": "instance"}))
41+ c.Assert(err, gc.IsNil)
42+ env, err := environs.New(cfg)
43+ c.Assert(err, gc.IsNil)
44+ instanceName := "100"
45+ inst, _ := testing.AssertStartInstance(c, env, instanceName)
46+ // Openstack now has three security groups for the server, the default
47+ // group, one group for the entire environment, and another for the
48+ // new instance.
49+ assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)})
50+ err = env.StopInstances([]instance.Instance{inst})
51+ c.Assert(err, gc.IsNil)
52+ // The security group for this instance is now removed.
53+ assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", env.Name())})
54+}
55+
56+// Due to bug #1300755 it can happen that the security group intended for
57+// an instance is also used as the common security group of another
58+// environment. If this is the case, the attempt to delete the instance's
59+// security group fails but StopInstance succeeds.
60+func (s *localServerSuite) TestStopInstanceSecurityGroupNotDeleted(c *gc.C) {
61+ // Force an error when a security group is deleted.
62+ cleanup := s.srv.Service.Nova.RegisterControlPoint(
63+ "removeSecurityGroup",
64+ func(sc hook.ServiceControl, args ...interface{}) error {
65+ return fmt.Errorf("failed on purpose")
66+ },
67+ )
68+ defer cleanup()
69+ cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
70+ "firewall-mode": "instance"}))
71+ c.Assert(err, gc.IsNil)
72+ env, err := environs.New(cfg)
73+ c.Assert(err, gc.IsNil)
74+ instanceName := "100"
75+ inst, _ := testing.AssertStartInstance(c, env, instanceName)
76+ allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)}
77+ assertSecurityGroups(c, env, allSecurityGroups)
78+ err = env.StopInstances([]instance.Instance{inst})
79+ c.Assert(err, gc.IsNil)
80+ assertSecurityGroups(c, env, allSecurityGroups)
81+}
82+
83+func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeInstance(c *gc.C) {
84+ cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
85+ "firewall-mode": "instance"}))
86+ c.Assert(err, gc.IsNil)
87+ env, err := environs.New(cfg)
88+ c.Assert(err, gc.IsNil)
89+ instanceName := "100"
90+ testing.AssertStartInstance(c, env, instanceName)
91+ allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)}
92+ assertSecurityGroups(c, env, allSecurityGroups)
93+ err = env.Destroy()
94+ c.Check(err, gc.IsNil)
95+ assertSecurityGroups(c, env, []string{"default"})
96+}
97+
98+func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeGlobal(c *gc.C) {
99+ cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
100+ "firewall-mode": "global"}))
101+ c.Assert(err, gc.IsNil)
102+ env, err := environs.New(cfg)
103+ c.Assert(err, gc.IsNil)
104+ instanceName := "100"
105+ testing.AssertStartInstance(c, env, instanceName)
106+ allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-global", env.Name())}
107+ assertSecurityGroups(c, env, allSecurityGroups)
108+ err = env.Destroy()
109+ c.Check(err, gc.IsNil)
110+ assertSecurityGroups(c, env, []string{"default"})
111+}
112+
113 var instanceGathering = []struct {
114 ids []instance.Id
115 err error
116
117=== modified file 'provider/openstack/provider.go'
118--- provider/openstack/provider.go 2014-04-07 07:06:59 +0000
119+++ provider/openstack/provider.go 2014-04-08 15:52:29 +0000
120@@ -11,6 +11,7 @@
121 "io/ioutil"
122 "net/http"
123 "regexp"
124+ "strings"
125 "sync"
126 "time"
127
128@@ -851,15 +852,29 @@
129
130 func (e *environ) StopInstances(insts []instance.Instance) error {
131 ids := make([]instance.Id, len(insts))
132+ securityGroupNames := make([]string, len(insts))
133 for i, inst := range insts {
134 instanceValue, ok := inst.(*openstackInstance)
135 if !ok {
136 return errors.New("Incompatible instance.Instance supplied")
137 }
138 ids[i] = instanceValue.Id()
139+ openstackName := instanceValue.getServerDetail().Name
140+ lastDashPos := strings.LastIndex(openstackName, "-")
141+ if lastDashPos == -1 {
142+ return fmt.Errorf("cannot identify instance ID in openstack server name %q", openstackName)
143+ }
144+ securityGroupNames[i] = e.machineGroupName(openstackName[lastDashPos+1:])
145 }
146 logger.Debugf("terminating instances %v", ids)
147- return e.terminateInstances(ids)
148+ err := e.terminateInstances(ids)
149+ if err != nil {
150+ return err
151+ }
152+ if e.Config().FirewallMode() == config.FwInstance {
153+ return e.deleteSecurityGroups(securityGroupNames)
154+ }
155+ return nil
156 }
157
158 // collectInstances tries to get information on each instance id in ids.
159@@ -949,7 +964,29 @@
160 }
161
162 func (e *environ) Destroy() error {
163- return common.Destroy(e)
164+ err := common.Destroy(e)
165+ if err != nil {
166+ return err
167+ }
168+ novaClient := e.nova()
169+ securityGroups, err := novaClient.ListSecurityGroups()
170+ if err != nil {
171+ return err
172+ }
173+ re, err := regexp.Compile(fmt.Sprintf("^%s(-\\d+)?$", e.jujuGroupName()))
174+ if err != nil {
175+ return err
176+ }
177+ globalGroupName := e.globalGroupName()
178+ for _, group := range securityGroups {
179+ if re.MatchString(group.Name) || group.Name == globalGroupName {
180+ err = novaClient.DeleteSecurityGroup(group.Id)
181+ if err != nil {
182+ logger.Warningf("cannot delete security group %q. Used by another environment?", group.Name)
183+ }
184+ }
185+ }
186+ return nil
187 }
188
189 func (e *environ) globalGroupName() string {
190@@ -1207,6 +1244,29 @@
191 return *group, nil
192 }
193
194+// deleteSecurityGroups deletes the given security groups. If a security
195+// group is also used by another environment (see bug #1300755), an attempt
196+// to delete this group fails. A warning is logged in this case.
197+func (e *environ) deleteSecurityGroups(securityGroupNames []string) error {
198+ novaclient := e.nova()
199+ allSecurityGroups, err := novaclient.ListSecurityGroups()
200+ if err != nil {
201+ return err
202+ }
203+ for _, securityGroup := range allSecurityGroups {
204+ for _, name := range securityGroupNames {
205+ if securityGroup.Name == name {
206+ err := novaclient.DeleteSecurityGroup(securityGroup.Id)
207+ if err != nil {
208+ logger.Warningf("cannot delete security group %q. Used by another environment?", name)
209+ }
210+ break
211+ }
212+ }
213+ }
214+ return nil
215+}
216+
217 func (e *environ) terminateInstances(ids []instance.Id) error {
218 if len(ids) == 0 {
219 return nil

Subscribers

People subscribed via source and target branches

to status/vote changes: