Merge lp:~thomir-deactivatedaccount/adt-continuous-deployer/trunk-fix-reaper into lp:adt-continuous-deployer

Proposed by Thomi Richards
Status: Merged
Approved by: Celso Providelo
Approved revision: 67
Merged at revision: 67
Proposed branch: lp:~thomir-deactivatedaccount/adt-continuous-deployer/trunk-fix-reaper
Merge into: lp:adt-continuous-deployer
Diff against target: 138 lines (+60/-16)
3 files modified
ci_automation/branch.py (+1/-1)
ci_automation/juju.py (+25/-15)
ci_automation/nova.py (+34/-0)
To merge this branch: bzr merge lp:~thomir-deactivatedaccount/adt-continuous-deployer/trunk-fix-reaper
Reviewer Review Type Date Requested Status
Celso Providelo (community) Approve
Review via email: mp+263047@code.launchpad.net

Commit message

Several improvements to repaer.py to try and make it more reliable.

Description of the change

Several improvements to repaer.py to try and make it more reliable.

To post a comment you must log in.
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

This has been tested on wendigo, and from my small sample size (3), appears to solve the issue we're seeing.

Revision history for this message
Celso Providelo (cprov) wrote :

Nice! way more robust for destroying envs, `nova delete` will never fail us as juju does.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ci_automation/branch.py'
2--- ci_automation/branch.py 2015-06-04 23:09:56 +0000
3+++ ci_automation/branch.py 2015-06-25 23:23:48 +0000
4@@ -26,6 +26,7 @@
5
6 from . import config
7
8+
9 def get_revno(url):
10 '''Get the current revision for a branch URL.
11
12@@ -139,4 +140,3 @@
13 """
14 identifiers = ["{}\t{}".format(k, v) for k, v in revno_dict.items()]
15 return '\n'.join(identifiers)
16-
17
18=== modified file 'ci_automation/juju.py'
19--- ci_automation/juju.py 2015-05-28 00:21:30 +0000
20+++ ci_automation/juju.py 2015-06-25 23:23:48 +0000
21@@ -112,9 +112,16 @@
22 # No juju environment here.
23 pass
24 else:
25- # FIXME or leave alone?
26 args = ['juju', 'destroy-environment', '-y', '--force', name]
27- subprocess.check_call(args, env=env)
28+ try:
29+ utils.check_call(args, env=env)
30+ except subprocess.CalledProcessError as e:
31+ print("juju destroy-environment failed with %s" % e)
32+ print("Manually deleting left-over instances...")
33+ # juju sometimes fails, so simulate what it does ourselves.
34+ remaining = nova.get_instances_for_environment(name)
35+ nova.delete_instances(remaining)
36+ print("Instances have been manually deleted")
37
38 # Ensure all related security-groups left behind by `juju` are removed,
39 # otherwise they will remain consuming resources (and quota).
40@@ -125,26 +132,29 @@
41 if name in line
42 ]
43
44- for secgroup in secgroups:
45- secgroup = secgroup.strip()
46- # Try harder because sometimes the instance retaining the secgroup
47- # takes sometime to be purged.
48- for i in range(3):
49+ # Try harder because sometimes the instance retaining the secgroup
50+ # takes sometime to be purged.
51+ for i in range(3):
52+ for secgroup in secgroups.copy():
53 try:
54 subprocess.check_call(
55 ['nova', 'secgroup-delete', secgroup],
56 stdout=subprocess.DEVNULL,
57 stderr=subprocess.DEVNULL
58 )
59+ secgroups.remove(secgroup)
60 except subprocess.CalledProcessError:
61- time.sleep(1)
62- else:
63- break
64- else:
65- print(
66- 'The security-group "{}" could not be removed. '
67- 'Please remove it manually.'.format(secgroup)
68- )
69+ print(
70+ "Error removing secgroup '{}'. "
71+ "It will be retried".format(secgroup)
72+ )
73+ time.sleep(1)
74+
75+ if secgroups:
76+ print(
77+ 'The security groups "{}" could not be removed. '
78+ 'Please remove them manually.'.format(', '.join(secgroups))
79+ )
80
81 shutil.rmtree(env_dir)
82
83
84=== modified file 'ci_automation/nova.py'
85--- ci_automation/nova.py 2015-06-19 09:33:20 +0000
86+++ ci_automation/nova.py 2015-06-25 23:23:48 +0000
87@@ -21,6 +21,8 @@
88 import stat
89 import subprocess
90
91+from . import utils
92+
93
94 def nova_list():
95 """Default 'nova list' accessor."""
96@@ -36,6 +38,29 @@
97 return output
98
99
100+def get_list_of_nova_instances(nova_output=None):
101+ """A generator that yields all the current nova instances."""
102+ if nova_output is None:
103+ nova_output = nova_list()
104+
105+ for line in io.StringIO(nova_output).readlines()[3:-1]:
106+ yield line.split('|')[2].strip()
107+
108+
109+def get_instances_for_environment(env_name):
110+ """get a list of all instnaces whose names start with 'env_name'.
111+
112+ This is a hacky way of getting all the nova instances that are part of
113+ a given juju environment.
114+
115+ """
116+ matched = []
117+ for instance_name in get_list_of_nova_instances():
118+ if instance_name.strip().startswith(env_name):
119+ matched.append(instance_name)
120+ return matched
121+
122+
123 def get_services_map(base_dir, nova_output=None):
124 """Return a services map dictionary.
125
126@@ -146,3 +171,12 @@
127 # If there is no network key (unlikely), treat it as not having a floating
128 # ip:
129 return False
130+
131+
132+def delete_instances(instance_list):
133+ for i in instance_list:
134+ delete_instance(i)
135+
136+
137+def delete_instance(instance_name):
138+ utils.check_call(['nova', 'delete', instance_name])

Subscribers

People subscribed via source and target branches

to all changes: