Merge lp:~paulgear/charms/trusty/grafana/layer-grafana-reactive-states-fix into lp:~canonical-is-sa/charms/trusty/grafana/layer-grafana

Proposed by Paul Gear
Status: Merged
Approved by: Nick Moffitt
Approved revision: 48
Merged at revision: 41
Proposed branch: lp:~paulgear/charms/trusty/grafana/layer-grafana-reactive-states-fix
Merge into: lp:~canonical-is-sa/charms/trusty/grafana/layer-grafana
Diff against target: 228 lines (+74/-33)
3 files modified
README.md (+2/-2)
layer.yaml (+1/-0)
reactive/grafana.py (+71/-31)
To merge this branch: bzr merge lp:~paulgear/charms/trusty/grafana/layer-grafana-reactive-states-fix
Reviewer Review Type Date Requested Status
Stuart Bishop (community) Approve
Nick Moffitt (community) Approve
Review via email: mp+300547@code.launchpad.net
To post a comment you must log in.
43. By Paul Gear

Latest stable

44. By Paul Gear

Add repo to keep charm build happy

45. By Paul Gear

Revert repo change; contra https://packagecloud.io/grafana/stable/install#manual, trusty does not exist

46. By Paul Gear

Add upgrade charm hook

47. By Paul Gear

Prevent race in calling add_backup_api_keys()

48. By Paul Gear

Clarify reason for commented-out state

Revision history for this message
Nick Moffitt (nick-moffitt) wrote :

I've been thinking out loud a lot about this merge while in the office, and it boils down to basically one thing that's sorely needed when using the reactive model:

  Locking semantics.

The (now deprecated, I hope) only_once decorator is the most obvious example of this. Unfortunately the implementation doesn't fit our needs, but the desire it expresses is a good one. What you've gone through and done for a lot of this is to add "When not Y" to a lot of "When X" conditions. Again, this ensures that the non-idempotent steps are only run once.

I have always fought for idempotent state-enforcing changes, but accept that those aren't always possible. I'm not sure what the Right Thing is, but this change is definitely not Wrong!

review: Approve
49. By Paul Gear

Update nagios service check per charm helpers MP

50. By Paul Gear

Run init script directly

Revision history for this message
Stuart Bishop (stub) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README.md'
--- README.md 2016-05-13 09:43:01 +0000
+++ README.md 2016-07-21 02:47:52 +0000
@@ -1,6 +1,6 @@
1#Overview1#Overview
22
3This charm provides the latest version of Grafana.3This charm provides the latest stable version of Grafana.
44
5#Usage5#Usage
66
@@ -9,7 +9,7 @@
99
10Above will automatcially configure prometheus as grafana datasource10Above will automatcially configure prometheus as grafana datasource
1111
12If admin password is not set using configuration option it is autogenated.12If admin password is not set using configuration option it is autogenerated.
13To retried autogenerated password run:13To retried autogenerated password run:
14 juju run --service grafana "scripts/get_admin_password"14 juju run --service grafana "scripts/get_admin_password"
1515
1616
=== modified file 'layer.yaml'
--- layer.yaml 2016-04-29 22:11:08 +0000
+++ layer.yaml 2016-07-21 02:47:52 +0000
@@ -1,2 +1,3 @@
1includes: ['layer:basic', 'interface:nrpe-external-master', 'interface:grafana-source', 'interface:http']1includes: ['layer:basic', 'interface:nrpe-external-master', 'interface:grafana-source', 'interface:http']
2ignore: ['.*.swp' ]2ignore: ['.*.swp' ]
3repo: bzr+ssh://bazaar.launchpad.net/~canonical-is-sa/charms/trusty/grafana/layer-grafana/
34
=== modified file 'reactive/grafana.py'
--- reactive/grafana.py 2016-07-14 15:07:36 +0000
+++ reactive/grafana.py 2016-07-21 02:47:52 +0000
@@ -7,12 +7,11 @@
7import json7import json
8import subprocess8import subprocess
9import base649import base64
10from time import sleep
11from charmhelpers import fetch10from charmhelpers import fetch
12from charmhelpers.core import host, hookenv, unitdata11from charmhelpers.core import host, hookenv, unitdata
13from charmhelpers.core.templating import render12from charmhelpers.core.templating import render
14from charmhelpers.contrib.charmsupport import nrpe13from charmhelpers.contrib.charmsupport import nrpe
15from charms.reactive import when, when_not, set_state, only_once14from charms.reactive import hook, remove_state, set_state, when, when_not
16from charms.reactive.helpers import any_file_changed, data_changed15from charms.reactive.helpers import any_file_changed, data_changed
1716
1817
@@ -39,9 +38,11 @@
39DASHBOARDS_BACKUP_CRON_TMPL = 'juju-dashboards-backup.j2'38DASHBOARDS_BACKUP_CRON_TMPL = 'juju-dashboards-backup.j2'
4039
4140
41@when_not('grafana.installed')
42def install_packages():42def install_packages():
43 config = hookenv.config()43 config = hookenv.config()
44 install_opts = ('install_sources', 'install_keys')44 install_opts = ('install_sources', 'install_keys')
45 hookenv.status_set('maintenance', 'Installing grafana')
45 if config.changed('install_file') and config.get('install_file', False):46 if config.changed('install_file') and config.get('install_file', False):
46 hookenv.status_set('maintenance', 'Installing deb pkgs')47 hookenv.status_set('maintenance', 'Installing deb pkgs')
47 fetch.apt_install(GRAFANA_DEPS)48 fetch.apt_install(GRAFANA_DEPS)
@@ -56,7 +57,25 @@
56 packages = ['grafana']57 packages = ['grafana']
57 fetch.configure_sources(update=True)58 fetch.configure_sources(update=True)
58 fetch.apt_install(packages)59 fetch.apt_install(packages)
59 hookenv.status_set('maintenance', 'Waiting for start')60 set_state('grafana.installed')
61 hookenv.status_set('active', 'Completed installing grafana')
62
63
64@hook('upgrade-charm')
65def upgrade_charm():
66 hookenv.status_set('maintenance', 'Forcing package update and reconfiguration on upgrade-charm')
67 remove_state('grafana.installed')
68 remove_state('grafana.configured')
69
70
71@hook('config.changed')
72def config_changed():
73 remove_state('grafana.configured')
74 config = hookenv.config()
75 if config.changed('dashboards_backup_schedule') or config.changed('dashboards_backup_dir'):
76 remove_state('grafana.backup.configured')
77 if config.changed('admin_password') or config.changed('nagios_context'):
78 remove_state('grafana.admin_password.set')
6079
6180
62def check_ports(new_port):81def check_ports(new_port):
@@ -97,8 +116,8 @@
97 if select_query('SELECT id FROM api_key WHERE org_id=? AND name=?', [org_id, name]):116 if select_query('SELECT id FROM api_key WHERE org_id=? AND name=?', [org_id, name]):
98 hookenv.log('API key {} in org {} already exists, skipping'.format(name, org_id))117 hookenv.log('API key {} in org {} already exists, skipping'.format(name, org_id))
99 continue118 continue
100 j = {'n': name,119 j = {'n': name,
101 'k': passwd,120 'k': passwd,
102 'id': org_id}121 'id': org_id}
103 encoded = base64.b64encode(json.dumps(j).encode('ascii')).decode('ascii')122 encoded = base64.b64encode(json.dumps(j).encode('ascii')).decode('ascii')
104 stmt = 'INSERT INTO api_key (org_id, name, key, role, created, updated)' + \123 stmt = 'INSERT INTO api_key (org_id, name, key, role, created, updated)' + \
@@ -119,10 +138,10 @@
119 return kv.get('grafana.dashboards_backup_keys')138 return kv.get('grafana.dashboards_backup_keys')
120139
121140
122@when_not('grafana.started')141@when('grafana.installed')
142@when_not('grafana.configured')
123def setup_grafana():143def setup_grafana():
124 hookenv.status_set('maintenance', 'Configuring grafana')144 hookenv.status_set('maintenance', 'Configuring grafana')
125 install_packages()
126 config = hookenv.config()145 config = hookenv.config()
127 settings = {'config': config}146 settings = {'config': config}
128 render(source=GRAFANA_INI_TMPL,147 render(source=GRAFANA_INI_TMPL,
@@ -131,7 +150,23 @@
131 owner='root', group='grafana',150 owner='root', group='grafana',
132 perms=0o640,151 perms=0o640,
133 )152 )
153 check_ports(config.get('port'))
154 set_state('grafana.configured')
155 remove_state('grafana.started')
156 hookenv.status_set('active', 'Completed configuring grafana')
157
158
159@when('grafana.started')
160@when_not('grafana.backup.configured')
161def setup_backup_shedule():
162 # copy script, create cronjob, ensure directory exists
163
164 # XXX: If you add any dependencies on config items here,
165 # be sure to update config_changed() accordingly!
166
167 config = hookenv.config()
134 if config.get('dashboards_backup_schedule', False):168 if config.get('dashboards_backup_schedule', False):
169 hookenv.status_set('maintenance', 'Configuring grafana dashboard backup')
135 hookenv.log('Setting up dashboards backup job...')170 hookenv.log('Setting up dashboards backup job...')
136 host.rsync('files/dashboards_backup', '/usr/local/bin/dashboards_backup')171 host.rsync('files/dashboards_backup', '/usr/local/bin/dashboards_backup')
137 host.mkdir(config.get('dashboards_backup_dir'))172 host.mkdir(config.get('dashboards_backup_dir'))
@@ -144,37 +179,29 @@
144 owner='root', group='root',179 owner='root', group='root',
145 perms=0o640,180 perms=0o640,
146 )181 )
147 # copy script, create cronjob, ensure directory exists182 hookenv.status_set('active', 'Completed configuring grafana dashboard backup')
148 check_ports(config.get('port'))183 set_state('grafana.backup.configured')
149 set_state('grafana.start')184
150 hookenv.status_set('active', 'Ready')185
151186@when('grafana.configured')
152187@when_not('grafana.started')
153@when('grafana.started')
154def check_config():
155 if data_changed('grafana.config', hookenv.config()):
156 setup_grafana() # reconfigure and restart
157 db_init()
158
159
160@when('grafana.start')
161def restart_grafana():188def restart_grafana():
162 if not host.service_running(SVCNAME):189 if not host.service_running(SVCNAME):
163 hookenv.log('Starting {}...'.format(SVCNAME))190 msg = 'Starting {}'.format(SVCNAME)
191 hookenv.status_set('maintenance', msg)
192 hookenv.log(msg)
164 host.service_start(SVCNAME)193 host.service_start(SVCNAME)
165 elif any_file_changed([GRAFANA_INI]):194 elif any_file_changed([GRAFANA_INI]):
166 hookenv.log('Restarting {}, config file changed...'.format(SVCNAME))195 msg = 'Restarting {}'.format(SVCNAME)
196 hookenv.log(msg)
197 hookenv.status_set('maintenance', msg)
167 host.service_restart(SVCNAME)198 host.service_restart(SVCNAME)
168 hookenv.status_set('active', 'Ready')199 hookenv.status_set('active', 'Ready')
169 set_state('grafana.started')200 set_state('grafana.started')
170201 hookenv.status_set('active', 'Started {}'.format(SVCNAME))
171202
172@only_once203
173def db_init():204@when('grafana.started')
174 sleep(10)
175 check_adminuser()
176
177
178@when('nrpe-external-master.available')205@when('nrpe-external-master.available')
179def update_nrpe_config(svc):206def update_nrpe_config(svc):
180 # python-dbus is used by check_upstart_job207 # python-dbus is used by check_upstart_job
@@ -185,6 +212,12 @@
185 nrpe.add_init_service_checks(nrpe_setup, SVCNAME, current_unit)212 nrpe.add_init_service_checks(nrpe_setup, SVCNAME, current_unit)
186 nrpe_setup.write()213 nrpe_setup.write()
187214
215 # XXX: Update this when https://code.launchpad.net/~paulgear/charm-helpers/nrpe-service-immediate-check/+merge/300682 is merged.
216 output = open('/var/lib/nagios/service-check-grafana.txt', 'w')
217 cmd = '/usr/local/lib/nagios/plugins/check_exit_status.pl -s /etc/init.d/grafana'
218 ret = subprocess.call(cmd.split(), stdout=output, stderr=subprocess.STDOUT)
219 hookenv.log('{} returned {}'.format(cmd, ret))
220
188221
189@when_not('nrpe-external-master.available')222@when_not('nrpe-external-master.available')
190def wipe_nrpe_checks():223def wipe_nrpe_checks():
@@ -326,6 +359,8 @@
326 return (stmt, values)359 return (stmt, values)
327360
328361
362@when('grafana.started')
363@when_not('grafana.admin_password.set')
329def check_adminuser():364def check_adminuser():
330 """365 """
331 CREATE TABLE `user` (366 CREATE TABLE `user` (
@@ -347,6 +382,10 @@
347 );382 );
348 INSERT INTO "user" VALUES(1,0,'admin','root+bootstack-ps45@canonical.com','BootStack Team','309bc4e78bc60d02dc0371d9e9fa6bf9a809d5dc25c745b9e3f85c3ed49c6feccd4ffc96d1db922f4297663a209e93f7f2b6','LZeJ3nSdrC','hseJcLcnPN','',1,1,0,'light','2016-01-22 12:00:08','2016-01-22 12:02:13');383 INSERT INTO "user" VALUES(1,0,'admin','root+bootstack-ps45@canonical.com','BootStack Team','309bc4e78bc60d02dc0371d9e9fa6bf9a809d5dc25c745b9e3f85c3ed49c6feccd4ffc96d1db922f4297663a209e93f7f2b6','LZeJ3nSdrC','hseJcLcnPN','',1,1,0,'light','2016-01-22 12:00:08','2016-01-22 12:02:13');
349 """384 """
385
386 # XXX: If you add any dependencies on config items here,
387 # be sure to update config_changed() accordingly!
388
350 config = hookenv.config()389 config = hookenv.config()
351 passwd = config.get('admin_password', False)390 passwd = config.get('admin_password', False)
352 if not passwd:391 if not passwd:
@@ -380,6 +419,7 @@
380 except sqlite3.OperationalError as e:419 except sqlite3.OperationalError as e:
381 hookenv.log('check_adminuser::sqlite3.OperationError: {}'.format(e))420 hookenv.log('check_adminuser::sqlite3.OperationError: {}'.format(e))
382 return421 return
422 set_state('grafana.admin_password.set')
383423
384424
385def hpwgen(passwd, salt):425def hpwgen(passwd, salt):

Subscribers

People subscribed via source and target branches

to all changes: