Merge lp:~thumper/python-jujuclient/jes-cache-file into lp:python-jujuclient

Proposed by Tim Penhey
Status: Superseded
Proposed branch: lp:~thumper/python-jujuclient/jes-cache-file
Merge into: lp:python-jujuclient
Diff against target: 202 lines (+137/-4)
3 files modified
.bzrignore (+1/-0)
jujuclient.py (+35/-3)
test_jujuclient.py (+101/-1)
To merge this branch: bzr merge lp:~thumper/python-jujuclient/jes-cache-file
Reviewer Review Type Date Requested Status
juju-deployers Pending
Review via email: mp+261586@code.launchpad.net

This proposal has been superseded by a proposal from 2015-07-17.

Description of the change

With the jes feature flag, we now don't store environment details in .jenv files, but in a single cache.yaml file.

This branch teaches python-jujuclient how to get the required information out of it.

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

I have not been able to run the tests for some reason, and I am unfamiliar with tox:

$ JUJU_TEST_ENV="mary2" tox -e py27
Traceback (most recent call last):
  File "/usr/bin/tox", line 9, in <module>
    load_entry_point('tox==1.6.0', 'console_scripts', 'tox')()
  File "/usr/lib/python2.7/dist-packages/tox/_cmdline.py", line 25, in main
    config = parseconfig(args, 'tox')
  File "/usr/lib/python2.7/dist-packages/tox/_config.py", line 44, in parseconfig
    parseini(config, inipath)
  File "/usr/lib/python2.7/dist-packages/tox/_config.py", line 236, in __init__
    config)
  File "/usr/lib/python2.7/dist-packages/tox/_config.py", line 292, in _makeenvconfig
    vc.setenv = reader.getdict(section, 'setenv')
  File "/usr/lib/python2.7/dist-packages/tox/_config.py", line 419, in getdict
    s = self.getdefault(section, name, None)
  File "/usr/lib/python2.7/dist-packages/tox/_config.py", line 511, in getdefault
    x = self._replace(x)
  File "/usr/lib/python2.7/dist-packages/tox/_config.py", line 618, in _replace
    return rexpattern.sub(replace_func, x)
  File "/usr/lib/python2.7/dist-packages/tox/_config.py", line 609, in _replace_match_no_quote
    return self._replace_match(match, quote=False)
  File "/usr/lib/python2.7/dist-packages/tox/_config.py", line 604, in _replace_match
    return handler(match, quote)
  File "/usr/lib/python2.7/dist-packages/tox/_config.py", line 541, in _replace_env
    (envkey, envkey))
tox.ConfigError: ConfigError: substitution env:'JUJU_TEST_ENV:"test"': unkown environment variable 'JUJU_TEST_ENV:"test"'

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

talked with tim on irc, think he worked through this

Revision history for this message
Tim Penhey (thumper) wrote :

Managed to get the tests running, but they fail, even for trunk.

https://bugs.launchpad.net/python-jujuclient/+bug/1465084

61. By Tim Penhey

Look for the cache file.

62. By Tim Penhey

Merge trunk.

63. By Tim Penhey

add tests

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2015-07-09 20:46:45 +0000
+++ .bzrignore 2015-07-17 05:34:42 +0000
@@ -13,3 +13,4 @@
13.tox13.tox
14.coverage14.coverage
15juju-backup-20150709-202028.tar.gz15juju-backup-20150709-202028.tar.gz
16__pycache__
1617
=== modified file 'jujuclient.py'
--- jujuclient.py 2015-07-16 22:19:20 +0000
+++ jujuclient.py 2015-07-17 05:34:42 +0000
@@ -207,16 +207,48 @@
207 return s207 return s
208208
209 def parse_env(self, env_name):209 def parse_env(self, env_name):
210 import yaml
211 jhome = os.path.expanduser(210 jhome = os.path.expanduser(
212 os.environ.get('JUJU_HOME', '~/.juju'))211 os.environ.get('JUJU_HOME', '~/.juju'))
212
213 # Look in the cache file first.
214 cache_file = os.path.join(jhome, 'environments', 'cache.yaml')
213 jenv = os.path.join(jhome, 'environments', '%s.jenv' % env_name)215 jenv = os.path.join(jhome, 'environments', '%s.jenv' % env_name)
216
217 if os.path.exists(cache_file):
218 try:
219 return jhome, self.environment_from_cache(env_name, cache_file)
220 except EnvironmentNotBootstrapped:
221 pass
222 # Fall through to getting the info from the jenv
214 if not os.path.exists(jenv):223 if not os.path.exists(jenv):
215 raise EnvironmentNotBootstrapped(env_name)224 raise EnvironmentNotBootstrapped(env_name)
216225 return jhome, self.environment_from_jenv(jenv)
226
227 def environment_from_cache(self, env_name, cache_filename):
228 import yaml
229 with open(cache_filename) as fh:
230 data = yaml.safe_load(fh.read())
231 try:
232 # environment holds:
233 # user, env-uuid, server-uuid
234 environment = data['environment'][env_name]
235 server = data['server-data'][environment['server-uuid']]
236 return {
237 'user': environment['user'],
238 'password': server['identities'][environment['user']],
239 'environ-uuid': environment['env-uuid'],
240 'server-uuid': environment['server-uuid'],
241 'state-servers': server['api-endpoints'],
242 'ca-cert': server['ca-cert'],
243 }
244 except KeyError:
245 raise EnvironmentNotBootstrapped(env_name)
246
247 def environment_from_jenv(self, jenv):
248 import yaml
217 with open(jenv) as fh:249 with open(jenv) as fh:
218 data = yaml.safe_load(fh.read())250 data = yaml.safe_load(fh.read())
219 return jhome, data251 return data
220252
221 @staticmethod253 @staticmethod
222 def split_host_port(server):254 def split_host_port(server):
223255
=== modified file 'test_jujuclient.py'
--- test_jujuclient.py 2015-07-16 22:19:20 +0000
+++ test_jujuclient.py 2015-07-17 05:34:42 +0000
@@ -1,3 +1,4 @@
1import copy
1import errno2import errno
2import mock3import mock
3import os4import os
@@ -15,7 +16,10 @@
15 # Watch wrappers16 # Watch wrappers
16 WaitForNoMachines,17 WaitForNoMachines,
17 # Facades18 # Facades
18 Actions, Annotations, Backups, Charms, HA, KeyManager, UserManager)19 Actions, Annotations, Backups, Charms, HA, KeyManager, UserManager,
20 # Exceptions
21 EnvironmentNotBootstrapped,
22)
1923
2024
21ENV_NAME = os.environ.get("JUJU_TEST_ENV")25ENV_NAME = os.environ.get("JUJU_TEST_ENV")
@@ -24,6 +28,16 @@
24 raise ValueError("No Testing Environment Defined.")28 raise ValueError("No Testing Environment Defined.")
2529
2630
31SAMPLE_CONFIG = {
32 'user': 'tester',
33 'password': 'sekrit',
34 'environ-uuid': 'some-uuid',
35 'server-uuid': 'server-uuid',
36 'state-servers': ['localhost:12345'],
37 'ca-cert': 'test-cert',
38}
39
40
27def reset(env):41def reset(env):
28 status = env.status()42 status = env.status()
29 env.destroy_machines(43 env.destroy_machines(
@@ -57,6 +71,11 @@
5771
58class ClientConnectorTest(unittest.TestCase):72class ClientConnectorTest(unittest.TestCase):
5973
74 def mkdir(self):
75 d = tempfile.mkdtemp()
76 self.addCleanup(shutil.rmtree, d)
77 return d
78
60 @mock.patch('jujuclient.websocket')79 @mock.patch('jujuclient.websocket')
61 def test_connect_socket(self, websocket):80 def test_connect_socket(self, websocket):
62 address = "wss://abc:17070"81 address = "wss://abc:17070"
@@ -104,6 +123,87 @@
104 ValueError, Connector.split_host_port, 'I am not an ip/port combo'123 ValueError, Connector.split_host_port, 'I am not an ip/port combo'
105 )124 )
106125
126 def test_parse_env_missing(self):
127 temp_juju_home = self.mkdir()
128 with mock.patch.dict('os.environ', {'JUJU_HOME': temp_juju_home}):
129 connector = Connector()
130 self.assertRaises(
131 EnvironmentNotBootstrapped,
132 connector.parse_env,
133 'missing')
134
135 def test_parse_env_jenv(self):
136 temp_juju_home = self.mkdir()
137 self.write_jenv(temp_juju_home, 'test-env', SAMPLE_CONFIG)
138 with mock.patch.dict('os.environ', {'JUJU_HOME': temp_juju_home}):
139 connector = Connector()
140 home, env = connector.parse_env('test-env')
141 self.assertEqual(home, temp_juju_home)
142 self.assertEqual(env, SAMPLE_CONFIG)
143
144 def test_parse_cache_file(self):
145 temp_juju_home = self.mkdir()
146 self.write_cache_file(temp_juju_home, 'test-env', SAMPLE_CONFIG)
147 with mock.patch.dict('os.environ', {'JUJU_HOME': temp_juju_home}):
148 connector = Connector()
149 home, env = connector.parse_env('test-env')
150 self.assertEqual(home, temp_juju_home)
151 self.assertEqual(env, SAMPLE_CONFIG)
152
153 def test_parse_cache_file_missing_env(self):
154 """Create a valid cache file, but look for an environment that isn't there.
155 """
156 temp_juju_home = self.mkdir()
157 self.write_cache_file(temp_juju_home, 'test-env', SAMPLE_CONFIG)
158 with mock.patch.dict('os.environ', {'JUJU_HOME': temp_juju_home}):
159 connector = Connector()
160 self.assertRaises(
161 EnvironmentNotBootstrapped,
162 connector.parse_env,
163 'missing')
164
165 def test_parse_env_cache_file_first(self):
166 """The cache file has priority over a jenv file."""
167 temp_juju_home = self.mkdir()
168 content = copy.deepcopy(SAMPLE_CONFIG)
169 self.write_jenv(temp_juju_home, 'test-env', content)
170 # Now change the password.
171 content['password'] = 'new password'
172 self.write_cache_file(temp_juju_home, 'test-env', content)
173 with mock.patch.dict('os.environ', {'JUJU_HOME': temp_juju_home}):
174 connector = Connector()
175 home, env = connector.parse_env('test-env')
176 self.assertEqual(home, temp_juju_home)
177 self.assertEqual(env, content)
178
179 def write_jenv(self, juju_home, env_name, content):
180 env_dir = os.path.join(juju_home, 'environments')
181 if not os.path.exists(env_dir):
182 os.mkdir(env_dir)
183 jenv = os.path.join(env_dir, '%s.jenv' % env_name)
184 with open(jenv, 'w') as f:
185 yaml.dump(content, f, default_flow_style=False)
186
187 def write_cache_file(self, juju_home, env_name, content):
188 env_dir = os.path.join(juju_home, 'environments')
189 if not os.path.exists(env_dir):
190 os.mkdir(env_dir)
191 filename = os.path.join(env_dir, 'cache.yaml')
192 cache_content = {
193 'environment': {
194 env_name: {'env-uuid': content['environ-uuid'],
195 'server-uuid': content['server-uuid'],
196 'user': content['user']}},
197 'server-data': {
198 content['server-uuid']: {
199 'api-endpoints': content['state-servers'],
200 'ca-cert': content['ca-cert'],
201 'identities': {content['user']: content['password']}}},
202 # Explicitly don't care about 'server-user' here.
203 }
204 with open(filename, 'w') as f:
205 yaml.dump(cache_content, f, default_flow_style=False)
206
107207
108class KeyManagerTest(unittest.TestCase):208class KeyManagerTest(unittest.TestCase):
109 def setUp(self):209 def setUp(self):

Subscribers

People subscribed via source and target branches

to all changes: