Merge lp:~michael.nelson/charm-helpers/ansible-tags into lp:charm-helpers

Proposed by Michael Nelson
Status: Merged
Approved by: Michael Nelson
Approved revision: 100
Merged at revision: 98
Proposed branch: lp:~michael.nelson/charm-helpers/ansible-tags
Merge into: lp:charm-helpers
Diff against target: 627 lines (+332/-208)
7 files modified
charmhelpers/contrib/ansible/__init__.py (+66/-3)
charmhelpers/contrib/saltstack/__init__.py (+3/-50)
charmhelpers/contrib/templating/contexts.py (+64/-0)
setup.py (+1/-0)
tests/contrib/ansible/test_ansible.py (+30/-0)
tests/contrib/saltstack/test_saltstates.py (+1/-155)
tests/contrib/templating/test_contexts.py (+167/-0)
To merge this branch: bzr merge lp:~michael.nelson/charm-helpers/ansible-tags
Reviewer Review Type Date Requested Status
James Page Approve
Review via email: mp+194324@code.launchpad.net

Commit message

Add tags to the ansible support and provide AnsibleHooks() helper.

Description of the change

This branch:
 * adds the ability to specify tags when playing an ansible playbook
 * adds an AnsibleHooks helper which can be used instead of the hookenv.Hooks helper, which works just like the hookenv.Hooks but additionally plays the configured playbook using the hook name as a tag, like this:

{{{
# Create the hooks helper, passing a list of hooks which will be
# handled by default by running all the sections of the playbook
# tagged with the hook name.
hooks = charmhelpers.contrib.ansible.AnsibleHooks(
    playbook_path='playbooks/site.yaml',
    default_hooks=[
        'start',
        'stop',
        'config-changed',
        'solr-relation-changed',
    ])

# All the tasks within the specified playbook tagged with 'install'
# will be run automatically after the call to install_ansible_support.
@hooks.hook()
def install():
    charmhelpers.contrib.ansible.install_ansible_support(from_ppa=True)
}}}

minimizing the amount of procedural code in hooks.py to be tested and maintained.

 * then a big chunk is just moving the juju_state_to_yaml helper and its tests out of saltstack (recommendation from last MP, as it's now used by both the saltstack and ansible helpers).

See line 534 of the (private, sorry) MP diff here for an example of the ansible hooks being used [1]

[1] https://code.launchpad.net/~michael.nelson/canonical-is-charms/bootstrap-7d-utils/+merge/191808

To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charmhelpers/contrib/ansible/__init__.py'
--- charmhelpers/contrib/ansible/__init__.py 2013-08-21 09:10:58 +0000
+++ charmhelpers/contrib/ansible/__init__.py 2013-11-07 14:38:44 +0000
@@ -95,7 +95,70 @@
95 hosts_file.write('localhost ansible_connection=local')95 hosts_file.write('localhost ansible_connection=local')
9696
9797
98def apply_playbook(playbook):98def apply_playbook(playbook, tags=None):
99 charmhelpers.contrib.saltstack.juju_state_to_yaml(99 tags = tags or []
100 tags = ",".join(tags)
101 charmhelpers.contrib.templating.contexts.juju_state_to_yaml(
100 ansible_vars_path, namespace_separator='__')102 ansible_vars_path, namespace_separator='__')
101 subprocess.check_call(['ansible-playbook', '-c', 'local', playbook])103 call = [
104 'ansible-playbook',
105 '-c',
106 'local',
107 playbook,
108 ]
109 if tags:
110 call.extend(['--tags', '{}'.format(tags)])
111 subprocess.check_call(call)
112
113
114class AnsibleHooks(charmhelpers.core.hookenv.Hooks):
115 """Run a playbook with the hook-name as the tag.
116
117 This helper builds on the standard hookenv.Hooks helper,
118 but additionally runs the playbook with the hook-name specified
119 using --tags (ie. running all the tasks tagged with the hook-name).
120
121 Example:
122 hooks = AnsibleHooks(playbook_path='playbooks/my_machine_state.yaml')
123
124 # All the tasks within my_machine_state.yaml tagged with 'install'
125 # will be run automatically after do_custom_work()
126 @hooks.hook()
127 def install():
128 do_custom_work()
129
130 # For most of your hooks, you won't need to do anything other
131 # than run the tagged tasks for the hook:
132 @hooks.hook('config-changed', 'start', 'stop')
133 def just_use_playbook():
134 pass
135
136 # As a convenience, you can avoid the above noop function by specifying
137 # the hooks which are handled by ansible-only and they'll be registered
138 # for you:
139 # hooks = AnsibleHooks(
140 # 'playbooks/my_machine_state.yaml',
141 # default_hooks=['config-changed', 'start', 'stop'])
142
143 if __name__ == "__main__":
144 # execute a hook based on the name the program is called by
145 hooks.execute(sys.argv)
146 """
147
148 def __init__(self, playbook_path, default_hooks=None):
149 """Register any hooks handled by ansible."""
150 super(AnsibleHooks, self).__init__()
151
152 self.playbook_path = playbook_path
153
154 default_hooks = default_hooks or []
155 noop = lambda *args, **kwargs: None
156 for hook in default_hooks:
157 self.register(hook, noop)
158
159 def execute(self, args):
160 """Execute the hook followed by the playbook using the hook as tag."""
161 super(AnsibleHooks, self).execute(args)
162 hook_name = os.path.basename(args[0])
163 charmhelpers.contrib.ansible.apply_playbook(
164 self.playbook_path, tags=[hook_name])
102165
=== modified file 'charmhelpers/contrib/saltstack/__init__.py'
--- charmhelpers/contrib/saltstack/__init__.py 2013-08-07 23:14:14 +0000
+++ charmhelpers/contrib/saltstack/__init__.py 2013-11-07 14:38:44 +0000
@@ -61,15 +61,13 @@
61#61#
62# Authors:62# Authors:
63# Charm Helpers Developers <juju@lists.ubuntu.com>63# Charm Helpers Developers <juju@lists.ubuntu.com>
64import os
65import subprocess64import subprocess
66import yaml
6765
66import charmhelpers.contrib.templating.contexts
68import charmhelpers.core.host67import charmhelpers.core.host
69import charmhelpers.core.hookenv68import charmhelpers.core.hookenv
7069
7170
72charm_dir = os.environ.get('CHARM_DIR', '')
73salt_grains_path = '/etc/salt/grains'71salt_grains_path = '/etc/salt/grains'
7472
7573
@@ -94,56 +92,11 @@
9492
95def update_machine_state(state_path):93def update_machine_state(state_path):
96 """Update the machine state using the provided state declaration."""94 """Update the machine state using the provided state declaration."""
97 juju_state_to_yaml(salt_grains_path)95 charmhelpers.contrib.templating.contexts.juju_state_to_yaml(
96 salt_grains_path)
98 subprocess.check_call([97 subprocess.check_call([
99 'salt-call',98 'salt-call',
100 '--local',99 '--local',
101 'state.template',100 'state.template',
102 state_path,101 state_path,
103 ])102 ])
104
105
106def juju_state_to_yaml(yaml_path, namespace_separator=':'):
107 """Update the juju config and state in a yaml file.
108
109 This includes any current relation-get data, and the charm
110 directory.
111 """
112 config = charmhelpers.core.hookenv.config()
113
114 # Add the charm_dir which we will need to refer to charm
115 # file resources etc.
116 config['charm_dir'] = charm_dir
117 config['local_unit'] = charmhelpers.core.hookenv.local_unit()
118
119 # Add any relation data prefixed with the relation type.
120 relation_type = charmhelpers.core.hookenv.relation_type()
121 if relation_type is not None:
122 relation_data = charmhelpers.core.hookenv.relation_get()
123 relation_data = dict(
124 ("{relation_type}{namespace_separator}{key}".format(
125 relation_type=relation_type.replace('-', '_'),
126 key=key,
127 namespace_separator=namespace_separator), val)
128 for key, val in relation_data.items())
129 config.update(relation_data)
130
131 # Don't use non-standard tags for unicode which will not
132 # work when salt uses yaml.load_safe.
133 yaml.add_representer(unicode, lambda dumper,
134 value: dumper.represent_scalar(
135 u'tag:yaml.org,2002:str', value))
136
137 yaml_dir = os.path.dirname(yaml_path)
138 if not os.path.exists(yaml_dir):
139 os.makedirs(yaml_dir)
140
141 if os.path.exists(yaml_path):
142 with open(yaml_path, "r") as existing_vars_file:
143 existing_vars = yaml.load(existing_vars_file.read())
144 else:
145 existing_vars = {}
146
147 existing_vars.update(config)
148 with open(yaml_path, "w+") as fp:
149 fp.write(yaml.dump(existing_vars))
150103
=== added file 'charmhelpers/contrib/templating/contexts.py'
--- charmhelpers/contrib/templating/contexts.py 1970-01-01 00:00:00 +0000
+++ charmhelpers/contrib/templating/contexts.py 2013-11-07 14:38:44 +0000
@@ -0,0 +1,64 @@
1# Copyright 2013 Canonical Ltd.
2#
3# Authors:
4# Charm Helpers Developers <juju@lists.ubuntu.com>
5"""A helper to create a yaml cache of config with namespaced relation data."""
6import os
7import yaml
8
9import charmhelpers.core.hookenv
10
11
12charm_dir = os.environ.get('CHARM_DIR', '')
13
14
15def juju_state_to_yaml(yaml_path, namespace_separator=':'):
16 """Update the juju config and state in a yaml file.
17
18 This includes any current relation-get data, and the charm
19 directory.
20
21 This function was created for the ansible and saltstack
22 support, as those libraries can use a yaml file to supply
23 context to templates, but it may be useful generally to
24 create and update an on-disk cache of all the config, including
25 previous relation data.
26 """
27 config = charmhelpers.core.hookenv.config()
28
29 # Add the charm_dir which we will need to refer to charm
30 # file resources etc.
31 config['charm_dir'] = charm_dir
32 config['local_unit'] = charmhelpers.core.hookenv.local_unit()
33
34 # Add any relation data prefixed with the relation type.
35 relation_type = charmhelpers.core.hookenv.relation_type()
36 if relation_type is not None:
37 relation_data = charmhelpers.core.hookenv.relation_get()
38 relation_data = dict(
39 ("{relation_type}{namespace_separator}{key}".format(
40 relation_type=relation_type.replace('-', '_'),
41 key=key,
42 namespace_separator=namespace_separator), val)
43 for key, val in relation_data.items())
44 config.update(relation_data)
45
46 # Don't use non-standard tags for unicode which will not
47 # work when salt uses yaml.load_safe.
48 yaml.add_representer(unicode, lambda dumper,
49 value: dumper.represent_scalar(
50 u'tag:yaml.org,2002:str', value))
51
52 yaml_dir = os.path.dirname(yaml_path)
53 if not os.path.exists(yaml_dir):
54 os.makedirs(yaml_dir)
55
56 if os.path.exists(yaml_path):
57 with open(yaml_path, "r") as existing_vars_file:
58 existing_vars = yaml.load(existing_vars_file.read())
59 else:
60 existing_vars = {}
61
62 existing_vars.update(config)
63 with open(yaml_path, "w+") as fp:
64 fp.write(yaml.dump(existing_vars))
065
=== modified file 'setup.py'
--- setup.py 2013-08-01 01:40:14 +0000
+++ setup.py 2013-11-07 14:38:44 +0000
@@ -27,6 +27,7 @@
27 "charmhelpers.contrib.saltstack",27 "charmhelpers.contrib.saltstack",
28 "charmhelpers.contrib.hahelpers",28 "charmhelpers.contrib.hahelpers",
29 "charmhelpers.contrib.jujugui",29 "charmhelpers.contrib.jujugui",
30 "charmhelpers.contrib.templating",
30 ],31 ],
31 'scripts': [32 'scripts': [
32 "bin/chlp",33 "bin/chlp",
3334
=== modified file 'tests/contrib/ansible/test_ansible.py'
--- tests/contrib/ansible/test_ansible.py 2013-08-21 09:10:58 +0000
+++ tests/contrib/ansible/test_ansible.py 2013-11-07 14:38:44 +0000
@@ -132,3 +132,33 @@
132 "wsgi_file__relation_key1": "relation_value1",132 "wsgi_file__relation_key1": "relation_value1",
133 "wsgi_file__relation_key2": "relation_value2",133 "wsgi_file__relation_key2": "relation_value2",
134 }, result)134 }, result)
135
136 def test_calls_with_tags(self):
137 charmhelpers.contrib.ansible.apply_playbook(
138 'playbooks/complete-state.yaml', tags=['install', 'somethingelse'])
139
140 self.mock_subprocess.check_call.assert_called_once_with([
141 'ansible-playbook', '-c', 'local', 'playbooks/complete-state.yaml',
142 '--tags', 'install,somethingelse' ])
143
144 def test_hooks_executes_playbook_with_tag(self):
145 hooks = charmhelpers.contrib.ansible.AnsibleHooks('my/playbook.yaml')
146 foo = mock.MagicMock()
147 hooks.register('foo', foo)
148
149 hooks.execute(['foo'])
150
151 self.assertEqual(foo.call_count, 1)
152 self.mock_subprocess.check_call.assert_called_once_with([
153 'ansible-playbook', '-c', 'local', 'my/playbook.yaml',
154 '--tags', 'foo' ])
155
156 def test_specifying_ansible_handled_hooks(self):
157 hooks = charmhelpers.contrib.ansible.AnsibleHooks(
158 'my/playbook.yaml', default_hooks=['start', 'stop'])
159
160 hooks.execute(['start'])
161
162 self.mock_subprocess.check_call.assert_called_once_with([
163 'ansible-playbook', '-c', 'local', 'my/playbook.yaml',
164 '--tags', 'start' ])
135165
=== modified file 'tests/contrib/saltstack/test_saltstates.py'
--- tests/contrib/saltstack/test_saltstates.py 2013-08-21 09:10:58 +0000
+++ tests/contrib/saltstack/test_saltstates.py 2013-11-07 14:38:44 +0000
@@ -3,11 +3,7 @@
3# Authors:3# Authors:
4# Charm Helpers Developers <juju@lists.ubuntu.com>4# Charm Helpers Developers <juju@lists.ubuntu.com>
5import mock5import mock
6import os
7import shutil
8import tempfile
9import unittest6import unittest
10import yaml
117
12import charmhelpers.contrib.saltstack8import charmhelpers.contrib.saltstack
139
@@ -56,7 +52,7 @@
56 self.mock_subprocess = patcher.start()52 self.mock_subprocess = patcher.start()
57 self.addCleanup(patcher.stop)53 self.addCleanup(patcher.stop)
5854
59 patcher = mock.patch('charmhelpers.contrib.saltstack.'55 patcher = mock.patch('charmhelpers.contrib.templating.contexts.'
60 'juju_state_to_yaml')56 'juju_state_to_yaml')
61 self.mock_config_2_grains = patcher.start()57 self.mock_config_2_grains = patcher.start()
62 self.addCleanup(patcher.stop)58 self.addCleanup(patcher.stop)
@@ -77,153 +73,3 @@
77 'states/install.yaml')73 'states/install.yaml')
7874
79 self.mock_config_2_grains.assert_called_once_with('/etc/salt/grains')75 self.mock_config_2_grains.assert_called_once_with('/etc/salt/grains')
80
81
82class JujuConfig2GrainsTestCase(unittest.TestCase):
83 def setUp(self):
84 super(JujuConfig2GrainsTestCase, self).setUp()
85
86 # Hookenv patches (a single patch to hookenv doesn't work):
87 patcher = mock.patch('charmhelpers.core.hookenv.config')
88 self.mock_config = patcher.start()
89 self.addCleanup(patcher.stop)
90 patcher = mock.patch('charmhelpers.core.hookenv.relation_get')
91 self.mock_relation_get = patcher.start()
92 self.mock_relation_get.return_value = {}
93 self.addCleanup(patcher.stop)
94 patcher = mock.patch('charmhelpers.core.hookenv.relation_type')
95 self.mock_relation_type = patcher.start()
96 self.mock_relation_type.return_value = None
97 self.addCleanup(patcher.stop)
98 patcher = mock.patch('charmhelpers.core.hookenv.local_unit')
99 self.mock_local_unit = patcher.start()
100 self.addCleanup(patcher.stop)
101
102 # patches specific to this test class.
103 etc_dir = tempfile.mkdtemp()
104 self.addCleanup(shutil.rmtree, etc_dir)
105 self.grain_path = os.path.join(etc_dir, 'salt', 'grains')
106
107 patcher = mock.patch.object(charmhelpers.contrib.saltstack,
108 'charm_dir', '/tmp/charm_dir')
109 patcher.start()
110 self.addCleanup(patcher.stop)
111
112 def test_output_with_empty_relation(self):
113 self.mock_config.return_value = {
114 'group_code_owner': 'webops_deploy',
115 'user_code_runner': 'ubunet',
116 }
117 self.mock_local_unit.return_value = "click-index/3"
118
119 charmhelpers.contrib.saltstack.juju_state_to_yaml(self.grain_path)
120
121 with open(self.grain_path, 'r') as grain_file:
122 result = yaml.load(grain_file.read())
123 self.assertEqual({
124 "charm_dir": "/tmp/charm_dir",
125 "group_code_owner": "webops_deploy",
126 "user_code_runner": "ubunet",
127 "local_unit": "click-index/3",
128 }, result)
129
130 def test_output_with_no_relation(self):
131 self.mock_config.return_value = {
132 'group_code_owner': 'webops_deploy',
133 'user_code_runner': 'ubunet',
134 }
135 self.mock_local_unit.return_value = "click-index/3"
136 self.mock_relation_get.return_value = None
137
138 charmhelpers.contrib.saltstack.juju_state_to_yaml(self.grain_path)
139
140 with open(self.grain_path, 'r') as grain_file:
141 result = yaml.load(grain_file.read())
142 self.assertEqual({
143 "charm_dir": "/tmp/charm_dir",
144 "group_code_owner": "webops_deploy",
145 "user_code_runner": "ubunet",
146 "local_unit": "click-index/3",
147 }, result)
148
149 def test_output_with_relation(self):
150 self.mock_config.return_value = {
151 'group_code_owner': 'webops_deploy',
152 'user_code_runner': 'ubunet',
153 }
154 self.mock_relation_type.return_value = 'wsgi-file'
155 self.mock_relation_get.return_value = {
156 'relation_key1': 'relation_value1',
157 'relation_key2': 'relation_value2',
158 }
159 self.mock_local_unit.return_value = "click-index/3"
160
161 charmhelpers.contrib.saltstack.juju_state_to_yaml(self.grain_path)
162
163 with open(self.grain_path, 'r') as grain_file:
164 result = yaml.load(grain_file.read())
165 self.assertEqual({
166 "charm_dir": "/tmp/charm_dir",
167 "group_code_owner": "webops_deploy",
168 "user_code_runner": "ubunet",
169 "wsgi_file:relation_key1": "relation_value1",
170 "wsgi_file:relation_key2": "relation_value2",
171 "local_unit": "click-index/3",
172 }, result)
173
174 def test_relation_with_separator(self):
175 self.mock_config.return_value = {
176 'group_code_owner': 'webops_deploy',
177 'user_code_runner': 'ubunet',
178 }
179 self.mock_relation_type.return_value = 'wsgi-file'
180 self.mock_relation_get.return_value = {
181 'relation_key1': 'relation_value1',
182 'relation_key2': 'relation_value2',
183 }
184 self.mock_local_unit.return_value = "click-index/3"
185
186 charmhelpers.contrib.saltstack.juju_state_to_yaml(
187 self.grain_path, namespace_separator='__')
188
189 with open(self.grain_path, 'r') as grain_file:
190 result = yaml.load(grain_file.read())
191 self.assertEqual({
192 "charm_dir": "/tmp/charm_dir",
193 "group_code_owner": "webops_deploy",
194 "user_code_runner": "ubunet",
195 "wsgi_file__relation_key1": "relation_value1",
196 "wsgi_file__relation_key2": "relation_value2",
197 "local_unit": "click-index/3",
198 }, result)
199
200 def test_updates_existing_values(self):
201 """Data stored in grains is retained.
202
203 This may be helpful so that templates can access information
204 from relations outside the current context.
205 """
206 os.makedirs(os.path.dirname(self.grain_path))
207 with open(self.grain_path, 'w+') as grain_file:
208 grain_file.write(yaml.dump({
209 'solr:hostname': 'example.com',
210 'user_code_runner': 'oldvalue',
211 }))
212
213 self.mock_config.return_value = charmhelpers.core.hookenv.Serializable({
214 'group_code_owner': 'webops_deploy',
215 'user_code_runner': 'newvalue',
216 })
217 self.mock_local_unit.return_value = "click-index/3"
218
219 charmhelpers.contrib.saltstack.juju_state_to_yaml(self.grain_path)
220
221 with open(self.grain_path, 'r') as grain_file:
222 result = yaml.load(grain_file.read())
223 self.assertEqual({
224 "charm_dir": "/tmp/charm_dir",
225 "group_code_owner": "webops_deploy",
226 "user_code_runner": "newvalue",
227 "local_unit": "click-index/3",
228 "solr:hostname": "example.com",
229 }, result)
23076
=== added file 'tests/contrib/templating/test_contexts.py'
--- tests/contrib/templating/test_contexts.py 1970-01-01 00:00:00 +0000
+++ tests/contrib/templating/test_contexts.py 2013-11-07 14:38:44 +0000
@@ -0,0 +1,167 @@
1# Copyright 2013 Canonical Ltd.
2#
3# Authors:
4# Charm Helpers Developers <juju@lists.ubuntu.com>
5import mock
6import os
7import shutil
8import tempfile
9import unittest
10import yaml
11
12import charmhelpers.contrib.templating
13
14
15class JujuState2YamlTestCase(unittest.TestCase):
16 def setUp(self):
17 super(JujuState2YamlTestCase, self).setUp()
18
19 # Hookenv patches (a single patch to hookenv doesn't work):
20 patcher = mock.patch('charmhelpers.core.hookenv.config')
21 self.mock_config = patcher.start()
22 self.addCleanup(patcher.stop)
23 patcher = mock.patch('charmhelpers.core.hookenv.relation_get')
24 self.mock_relation_get = patcher.start()
25 self.mock_relation_get.return_value = {}
26 self.addCleanup(patcher.stop)
27 patcher = mock.patch('charmhelpers.core.hookenv.relation_type')
28 self.mock_relation_type = patcher.start()
29 self.mock_relation_type.return_value = None
30 self.addCleanup(patcher.stop)
31 patcher = mock.patch('charmhelpers.core.hookenv.local_unit')
32 self.mock_local_unit = patcher.start()
33 self.addCleanup(patcher.stop)
34
35 # patches specific to this test class.
36 etc_dir = tempfile.mkdtemp()
37 self.addCleanup(shutil.rmtree, etc_dir)
38 self.context_path = os.path.join(etc_dir, 'some', 'context')
39
40 patcher = mock.patch.object(charmhelpers.contrib.templating.contexts,
41 'charm_dir', '/tmp/charm_dir')
42 patcher.start()
43 self.addCleanup(patcher.stop)
44
45 def test_output_with_empty_relation(self):
46 self.mock_config.return_value = {
47 'group_code_owner': 'webops_deploy',
48 'user_code_runner': 'ubunet',
49 }
50 self.mock_local_unit.return_value = "click-index/3"
51
52 charmhelpers.contrib.templating.contexts.juju_state_to_yaml(
53 self.context_path)
54
55 with open(self.context_path, 'r') as context_file:
56 result = yaml.load(context_file.read())
57 self.assertEqual({
58 "charm_dir": "/tmp/charm_dir",
59 "group_code_owner": "webops_deploy",
60 "user_code_runner": "ubunet",
61 "local_unit": "click-index/3",
62 }, result)
63
64 def test_output_with_no_relation(self):
65 self.mock_config.return_value = {
66 'group_code_owner': 'webops_deploy',
67 'user_code_runner': 'ubunet',
68 }
69 self.mock_local_unit.return_value = "click-index/3"
70 self.mock_relation_get.return_value = None
71
72 charmhelpers.contrib.templating.contexts.juju_state_to_yaml(
73 self.context_path)
74
75 with open(self.context_path, 'r') as context_file:
76 result = yaml.load(context_file.read())
77 self.assertEqual({
78 "charm_dir": "/tmp/charm_dir",
79 "group_code_owner": "webops_deploy",
80 "user_code_runner": "ubunet",
81 "local_unit": "click-index/3",
82 }, result)
83
84 def test_output_with_relation(self):
85 self.mock_config.return_value = {
86 'group_code_owner': 'webops_deploy',
87 'user_code_runner': 'ubunet',
88 }
89 self.mock_relation_type.return_value = 'wsgi-file'
90 self.mock_relation_get.return_value = {
91 'relation_key1': 'relation_value1',
92 'relation_key2': 'relation_value2',
93 }
94 self.mock_local_unit.return_value = "click-index/3"
95
96 charmhelpers.contrib.templating.contexts.juju_state_to_yaml(
97 self.context_path)
98
99 with open(self.context_path, 'r') as context_file:
100 result = yaml.load(context_file.read())
101 self.assertEqual({
102 "charm_dir": "/tmp/charm_dir",
103 "group_code_owner": "webops_deploy",
104 "user_code_runner": "ubunet",
105 "wsgi_file:relation_key1": "relation_value1",
106 "wsgi_file:relation_key2": "relation_value2",
107 "local_unit": "click-index/3",
108 }, result)
109
110 def test_relation_with_separator(self):
111 self.mock_config.return_value = {
112 'group_code_owner': 'webops_deploy',
113 'user_code_runner': 'ubunet',
114 }
115 self.mock_relation_type.return_value = 'wsgi-file'
116 self.mock_relation_get.return_value = {
117 'relation_key1': 'relation_value1',
118 'relation_key2': 'relation_value2',
119 }
120 self.mock_local_unit.return_value = "click-index/3"
121
122 charmhelpers.contrib.templating.contexts.juju_state_to_yaml(
123 self.context_path, namespace_separator='__')
124
125 with open(self.context_path, 'r') as context_file:
126 result = yaml.load(context_file.read())
127 self.assertEqual({
128 "charm_dir": "/tmp/charm_dir",
129 "group_code_owner": "webops_deploy",
130 "user_code_runner": "ubunet",
131 "wsgi_file__relation_key1": "relation_value1",
132 "wsgi_file__relation_key2": "relation_value2",
133 "local_unit": "click-index/3",
134 }, result)
135
136 def test_updates_existing_values(self):
137 """Data stored in the context file is retained.
138
139 This may be helpful so that templates can access information
140 from relations outside the current execution environment.
141 """
142 os.makedirs(os.path.dirname(self.context_path))
143 with open(self.context_path, 'w+') as context_file:
144 context_file.write(yaml.dump({
145 'solr:hostname': 'example.com',
146 'user_code_runner': 'oldvalue',
147 }))
148
149 self.mock_config.return_value = charmhelpers.core.hookenv.Serializable({
150 'group_code_owner': 'webops_deploy',
151 'user_code_runner': 'newvalue',
152 })
153 self.mock_local_unit.return_value = "click-index/3"
154
155 charmhelpers.contrib.templating.contexts.juju_state_to_yaml(
156 self.context_path)
157
158 with open(self.context_path, 'r') as context_file:
159 result = yaml.load(context_file.read())
160 self.assertEqual({
161 "charm_dir": "/tmp/charm_dir",
162 "group_code_owner": "webops_deploy",
163 "user_code_runner": "newvalue",
164 "local_unit": "click-index/3",
165 "solr:hostname": "example.com",
166 }, result)
167

Subscribers

People subscribed via source and target branches