Merge ~freyes/charm-grafana:bug/1872682 into charm-grafana:master
- Git
- lp:~freyes/charm-grafana
- bug/1872682
- Merge into master
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | ~freyes/charm-grafana:bug/1872682 | ||||
Merge into: | charm-grafana:master | ||||
Diff against target: |
982 lines (+454/-40) (has conflicts) 24 files modified
src/actions/create-user (+14/-6) src/files/dashboards_backup (+4/-1) src/layer.yaml (+8/-1) src/lib/charms/layer/grafana.py (+33/-3) src/reactive/grafana.py (+137/-15) src/requirements.txt (+1/-0) src/templates/grafana.ini.j2 (+7/-1) src/templates/juju-dashboards-backup.j2 (+1/-1) src/templates/sync-grafana-snap (+7/-0) src/tests/functional/requirements.txt (+6/-0) src/tests/functional/tests/bundles/bionic-snap-tls.yaml (+1/-0) src/tests/functional/tests/bundles/bionic-tls.yaml (+1/-0) src/tests/functional/tests/bundles/focal-snap-tls.yaml (+1/-0) src/tests/functional/tests/bundles/focal-tls.yaml (+1/-0) src/tests/functional/tests/bundles/overlays/bionic-snap-tls.yaml.j2 (+11/-0) src/tests/functional/tests/bundles/overlays/bionic-tls.yaml.j2 (+10/-0) src/tests/functional/tests/bundles/overlays/focal-snap-tls.yaml.j2 (+32/-0) src/tests/functional/tests/bundles/overlays/focal-tls.yaml.j2 (+31/-0) src/tests/functional/tests/bundles/overlays/xenial-tls.yaml.j2 (+12/-0) src/tests/functional/tests/bundles/xenial-tls.yaml (+1/-0) src/tests/functional/tests/test_grafana.py (+76/-12) src/tests/functional/tests/tests.yaml (+7/-0) src/tests/unit/test_grafana.py (+48/-0) src/tox.ini (+4/-0) Conflict in src/tests/functional/requirements.txt Conflict in src/tox.ini |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Liam Young (community) | Needs Fixing | ||
Alvaro Uria (community) | Needs Fixing | ||
BootStack Reviewers | Pending | ||
BootStack Reviewers | Pending | ||
Review via email: mp+397611@code.launchpad.net |
This proposal has been superseded by a proposal from 2021-08-06.
Commit message
Add tls-client layer to support HTTPS
Description of the change
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Felipe Reyes (freyes) wrote : | # |
I've been trying to run the full functional tests (make functional) without success, I'm getting consistently the following error now:
unit-prometheus-0: 15:56:11 WARNING unit.prometheus
a few hours ago the charmstore was giving "error 500" for certain charms (e.g. easyrsa). I will post the results here once I get to full successful run.
Drew Freiberger (afreiberger) wrote : | # |
we very much have had that problem throughout the release cycle. it's just a poke and hope game until snapstore does the right thing.
Felipe Reyes (freyes) wrote : | # |
I was running the functional test suite and got into this race condition:
2021-02-11 20:32:54 INFO juju-log certificates:21: Restarting grafana-server
2021-02-11 20:32:54 INFO juju-log certificates:21: Invoking reactive handler: reactive/
2021-02-11 20:32:54 INFO juju-log certificates:21: Found datasource: {'service_name': 'prometheus', 'type': 'prometheus', 'url': 'http://
'}
2021-02-11 20:32:54 INFO juju-log certificates:21: Datasource already exist, updating: prometheus - Juju generated source
2021-02-11 20:32:54 ERROR juju-log certificates:21: Hook error:
...
urllib3.
...
requests.
So it seems that the restart of the service is not blocking until it's available to serve requests. I will propose a different patch to improve this.
Alvaro Uria (aluria) wrote : | # |
Please find a comment inline (re: tls_client.
Alvaro Uria (aluria) wrote : | # |
* make lint failed with:
./tests/
* make unittests failed with:
tests/unit/
See output at: https:/
* make functional failed on the first bundle (tests/
2021-03-19 07:31:39 [ERROR] unit-grafana-0.log: 2021-03-19 07:31:37 WARNING install Exception: port 3000 is closed
Alvaro Uria (aluria) wrote : | # |
Added comment inline re: retry_on_exception not having a delay
Felipe Reyes (freyes) wrote : | # |
On Fri, 2021-03-19 at 08:29 +0000, Alvaro Uria wrote:
> * make lint failed with:
> ./tests/
> method
>
> * make unittests failed with:
> tests/unit/
> FAILED
>
> See output at: https:/
>
> * make functional failed on the first bundle
> (tests/
> 2021-03-19 07:31:39 [ERROR] unit-grafana-0.log: 2021-03-19 07:31:37
> WARNING install Exception: port 3000 is closed
This failure happens because there was no base_delay as you pointed out
in the retry_on_
>
--
Felipe Reyes
Software Sustaining Engineer @ Canonical
# Email: <email address hidden> (GPG:0x9B1FFF39)
# Launchpad: ~freyes | IRC: freyes
Felipe Reyes (freyes) wrote : | # |
Hi Alvaro, I just pushed a new version of the patch. make lint/unittests are OK, I'm running make functional now, but it will take a while :-) . Thanks for reviewing my patch.
Felipe Reyes (freyes) wrote : | # |
Has anyone been able to run a successful run of grafana functional testing recently?, not necessarily with this patch, but in general, I get different issues all the time (snap store, or the charm store, or stsstack, or just juju timing out)
Xav Paice (xavpaice) wrote : | # |
Functest run from the candidate/21.04 branch (a copy of master at the time of writing) https:/
Tests on this branch also passed functests on my environment for the first bundle, however there are a LOT of test runs to complete for this and without automation of the tests this will cause a delay since we're locked into a release cycle right now.
Once this is reviewed and merged to master, it'll be available on cs:~llama-
Xav Paice (xavpaice) wrote : | # |
Functest failed with:
2021-04-13 10:53:55 [ERROR] {'model_
2021-04-13 10:53:55 [ERROR] Model model_apt_install (zaza-2010f9813ac7)
Traceback (most recent call last):
File "/home/
sys.
File "/home/
func_
File "/home/
run_
File "/home/
deploy.deploy(
File "/home/
zaza.
File "/home/
return run(_run_it())
File "/home/
return task.result()
File "/home/
return await f(*args, **kwargs)
File "/home/
await model.block_until(
File "/home/
await utils.block_
File "/home/
await asyncio.
File "/usr/lib/
raise exceptions.
asyncio.
Bundle it was deploying: src/tests/
Liam Young (gnuoy) wrote : | # |
I've raised a PR against freyes's branch which includes fixes to the TimeoutError mentioned in the previous comment and the dependency on OpenStack clients mentioned before that.
https:/
James Troup (elmo) wrote : | # |
Marking this as rejected to get it out of the review queue. The work done on this branch landed with Liam's MP. Thanks Felipe and Liam!
Felipe Reyes (freyes) wrote : | # |
Hi James, if by "Liam's MP" you mean https:/
Is there some other MP in flight or pending to be created and I'm confusing things?
Best,
PS: I'm running "make functional"at the moment, still running, but not failures so far[1]
[0]Proposed branch: ~gnuoy/
Merge into: ~freyes/
[1] $ grep "Deploying bundle" functional.log
2021-08-04 23:03:50 [INFO] Deploying bundle '/home/
2021-08-04 23:27:32 [INFO] Deploying bundle '/home/
2021-08-04 23:51:24 [INFO] Deploying bundle '/home/
2021-08-05 00:14:42 [INFO] Deploying bundle '/home/
2021-08-05 00:35:21 [INFO] Deploying bundle '/home/
2021-08-05 00:57:14 [INFO] Deploying bundle '/home/
Liam Young (gnuoy) wrote : | # |
Looks like src/tox.ini needs fixing
Felipe Reyes (freyes) wrote : | # |
Weird, I'm not seeing that merge conflict locally, will give it a try in a fresh git clone.
re: functional tests, they were running fine, but due to a networking issue the full run couldn't complete - https:/
Felipe Reyes (freyes) wrote : | # |
This is merge of my branch into master -> https:/
ubuntu@
Cloning into 'charm-grafana'...
remote: Enumerating objects: 1757, done.
remote: Counting objects: 100% (1757/1757), done.
remote: Compressing objects: 100% (1009/1009), done.
remote: Total 1757 (delta 882), reused 902 (delta 469)
Receiving objects: 100% (1757/1757), 309.80 KiB | 433.00 KiB/s, done.
Resolving deltas: 100% (882/882), done.
ubuntu@
ubuntu@
origin https:/
origin https:/
ubuntu@
ubuntu@
remote: Enumerating objects: 84, done.
remote: Counting objects: 100% (84/84), done.
* [new branch] bug/1872682 -> freyes/bug/1872682
* [new branch] bug/1893137 -> freyes/bug/1893137
ubuntu@
Branch 'bug/1872682' set up to track remote branch 'bug/1872682' from 'freyes'.
Switched to a new branch 'bug/1872682'
ubuntu@
* bug/1872682
master
ubuntu@
Already up to date.
ubuntu@
On branch bug/1872682
Your branch is up to date with 'freyes/
nothing to commit, working tree clean
ubuntu@
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
ubuntu@
Automatic merge went well; stopped before committing as requested
ubuntu@
On branch master
Your branch is up to date with 'origin/master'.
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: src/actions/
modified: src/files/
modified: src/layer.yaml
modified: src/lib/
modified: src/reactive/
modified: src/requirement
modified: src/templates/
modified: src/templates/
new file: src/templates/
new file: src/tests/
new file: src/tests/
new file: src/tests/
new file: src/tests/
new file: src/tests/
Felipe Reyes (freyes) wrote : | # |
I could get a full run of "make functional" succeed, to achieve this I had to fix a race condition on the test_13_
summary of the tests execution:
2021-08-06 02:37:18 [INFO] Events:
Deploy Bundle:
Start: 1628230469.9741626
Finish: 1628230580.8524628
Elapsed Time: 110.87830018997192
PCT Of Run Time: 2
Prepare Environment:
Start: 1628230458.4041638
Finish: 1628230469.9739351
Elapsed Time: 11.56977128982544
PCT Of Run Time: 1
Test tests.test_
Start: 1628231751.6462507
Finish: 1628231776.0118518
Elapsed Time: 24.365601062774658
PCT Of Run Time: 1
Test tests.test_
Start: 1628231214.4372096
Finish: 1628231751.6461709
Elapsed Time: 537.2089612483978
PCT Of Run Time: 6
Test tests.test_
Start: 1628222783.1681652
Finish: 1628222887.3098197
Elapsed Time: 104.14165449142456
PCT Of Run Time: 2
Wait for Deployment:
Start: 1628230580.8525686
Finish: 1628231212.1852653
Elapsed Time: 631.3326966762543
PCT Of Run Time: 8
Metadata: {}
Full output can be found at https:/
[0] https:/
Felipe Reyes (freyes) wrote : | # |
I will re-submit my proposal, the commit ids shown in the "unmerged commits" are invalid
Unmerged commits in Launchpad
6e52a25... by Liam Young on 2021-06-30 Use easyrsa on bionic for xenial tests
5ca8ed8... by Liam Young on 2021-06-30 Bug fixes for enabling TLS
4c7b77b... by Felipe Reyes on 2021-02-05 Add tls-client layer to support HTTPS
versus
$ git log --oneline
229263c (HEAD -> bug/1872682, freyes/bug/1872682) Wait until /etc/cron.
817ed45 Replace assertTrue with assertIn, assertNotIn and assertEqual
7108c49 Use easyrsa on bionic for xenial tests
ef9711e Bug fixes for enabling TLS
c9d57f8 Add tls-client layer to support HTTPS
Unmerged commits
- 6e52a25... by Liam Young
-
Use easyrsa on bionic for xenial tests
Use easyrsa on bionic for xenial tests as easyrsa is currently
failing to install on xenial. - 5ca8ed8... by Liam Young
-
Bug fixes for enabling TLS
* Update create-user action to work with https
* Update backup to work with https
* Update functional test requirements to work-around zaza bug *1
* Update tests.yaml to expect easyrsa to come up with a workload
status message of 'Certificate Authority connected.'
* Do not use f strings as the charm needs to run on xenial. - 4c7b77b... by Felipe Reyes
-
Add tls-client layer to support HTTPS
This patch adds a new interface provided by the tls-client later. When related
to a certificates provider (e.g. EasyRSA) it will configure the daemon to
serve over HTTPS.Fixes-Bug: #1893137
Preview Diff
1 | diff --git a/src/actions/create-user b/src/actions/create-user | |||
2 | index ab9f96c..916534c 100755 | |||
3 | --- a/src/actions/create-user | |||
4 | +++ b/src/actions/create-user | |||
5 | @@ -12,18 +12,24 @@ from charmhelpers.core.hookenv import ( | |||
6 | 12 | log, | 12 | log, |
7 | 13 | ) | 13 | ) |
8 | 14 | 14 | ||
10 | 15 | from charms.layer.grafana import get_admin_password | 15 | from charms.layer.grafana import ( |
11 | 16 | get_admin_password, | ||
12 | 17 | get_protocol, | ||
13 | 18 | ) | ||
14 | 16 | 19 | ||
15 | 17 | action = "create-user" | 20 | action = "create-user" |
16 | 18 | 21 | ||
17 | 19 | admin_passwd = get_admin_password() | 22 | admin_passwd = get_admin_password() |
18 | 20 | 23 | ||
19 | 24 | # Talking to ourselves on localhost so don't worry about CAs | ||
20 | 25 | verify_ca = False | ||
21 | 26 | |||
22 | 21 | if admin_passwd is None: | 27 | if admin_passwd is None: |
23 | 22 | action_fail('Unable to retrieve password.') | 28 | action_fail('Unable to retrieve password.') |
24 | 23 | sys.exit(0) | 29 | sys.exit(0) |
25 | 24 | 30 | ||
26 | 25 | port = config('port') | 31 | port = config('port') |
28 | 26 | grafana = "http://localhost:%s" % (port) | 32 | grafana = "{}://localhost:{}".format(get_protocol(), port) |
29 | 27 | api_auth = ('admin', admin_passwd) | 33 | api_auth = ('admin', admin_passwd) |
30 | 28 | 34 | ||
31 | 29 | # http://docs.grafana.org/http_api/admin/#global-users | 35 | # http://docs.grafana.org/http_api/admin/#global-users |
32 | @@ -57,13 +63,13 @@ user_data = { | |||
33 | 57 | headers = {'Content-Type': 'application/json'} | 63 | headers = {'Content-Type': 'application/json'} |
34 | 58 | 64 | ||
35 | 59 | grafana_api_create_user_url = grafana + api_create_user_url | 65 | grafana_api_create_user_url = grafana + api_create_user_url |
36 | 60 | |||
37 | 61 | if requests.utils.urlparse(grafana_api_create_user_url).scheme: | 66 | if requests.utils.urlparse(grafana_api_create_user_url).scheme: |
38 | 62 | r_create = requests.post( | 67 | r_create = requests.post( |
39 | 63 | grafana_api_create_user_url, | 68 | grafana_api_create_user_url, |
40 | 64 | auth=api_auth, | 69 | auth=api_auth, |
41 | 65 | headers=headers, | 70 | headers=headers, |
43 | 66 | data=json.dumps(user_data) | 71 | data=json.dumps(user_data), |
44 | 72 | verify=verify_ca, | ||
45 | 67 | ) | 73 | ) |
46 | 68 | else: | 74 | else: |
47 | 69 | action_fail("Grafana url %s failed to parse!" % (grafana_api_create_user_url)) | 75 | action_fail("Grafana url %s failed to parse!" % (grafana_api_create_user_url)) |
48 | @@ -90,7 +96,8 @@ grafana_api_org_url = grafana + api_org_url | |||
49 | 90 | if requests.utils.urlparse(grafana_api_org_url).scheme: | 96 | if requests.utils.urlparse(grafana_api_org_url).scheme: |
50 | 91 | r_org = requests.get( | 97 | r_org = requests.get( |
51 | 92 | grafana_api_org_url, | 98 | grafana_api_org_url, |
53 | 93 | auth=api_auth | 99 | auth=api_auth, |
54 | 100 | verify=verify_ca | ||
55 | 94 | ) | 101 | ) |
56 | 95 | else: | 102 | else: |
57 | 96 | action_fail("Grafana url %s failed to parse" % (grafana_api_org_url)) | 103 | action_fail("Grafana url %s failed to parse" % (grafana_api_org_url)) |
58 | @@ -108,7 +115,8 @@ if r_org.status_code == 200: | |||
59 | 108 | grafana_api_org_user_url, | 115 | grafana_api_org_user_url, |
60 | 109 | auth=api_auth, | 116 | auth=api_auth, |
61 | 110 | headers=headers, | 117 | headers=headers, |
63 | 111 | data=json.dumps(org_user_data) | 118 | data=json.dumps(org_user_data), |
64 | 119 | verify=verify_ca | ||
65 | 112 | ) | 120 | ) |
66 | 113 | else: | 121 | else: |
67 | 114 | action_fail("Grafana url %s failed to parse" % (grafana_api_org_user_url)) | 122 | action_fail("Grafana url %s failed to parse" % (grafana_api_org_user_url)) |
68 | diff --git a/src/files/dashboards_backup b/src/files/dashboards_backup | |||
69 | index 30299a2..88c6310 100755 | |||
70 | --- a/src/files/dashboards_backup | |||
71 | +++ b/src/files/dashboards_backup | |||
72 | @@ -18,7 +18,7 @@ def get_backup_filename(directory, org_name, uri): | |||
73 | 18 | 18 | ||
74 | 19 | 19 | ||
75 | 20 | def main(args): | 20 | def main(args): |
77 | 21 | base_url = 'http://localhost:{port}/api/'.format(port=args.port) | 21 | base_url = '{scheme}://localhost:{port}/api/'.format(scheme=args.scheme, port=args.port) |
78 | 22 | for key in args.api_keys: | 22 | for key in args.api_keys: |
79 | 23 | headers = {'Authorization': 'Bearer {}'.format(key)} | 23 | headers = {'Authorization': 'Bearer {}'.format(key)} |
80 | 24 | org_name = requests.get(base_url+'org', headers=headers).json()['name'] | 24 | org_name = requests.get(base_url+'org', headers=headers).json()['name'] |
81 | @@ -38,6 +38,9 @@ if __name__ == '__main__': | |||
82 | 38 | parser.add_argument('-d', '--directory', help='Directory where to store backups', | 38 | parser.add_argument('-d', '--directory', help='Directory where to store backups', |
83 | 39 | default='/srv/backups') | 39 | default='/srv/backups') |
84 | 40 | parser.add_argument('-p', '--port', help='Port to access grafana API', default='3000') | 40 | parser.add_argument('-p', '--port', help='Port to access grafana API', default='3000') |
85 | 41 | parser.add_argument('-s', '--scheme', | ||
86 | 42 | help='Scheme to use to access grafana API e.g. http or https', | ||
87 | 43 | default='http') | ||
88 | 41 | parser.add_argument('api_keys', help='List of API keys to use for backups', nargs='+') | 44 | parser.add_argument('api_keys', help='List of API keys to use for backups', nargs='+') |
89 | 42 | args = parser.parse_args() | 45 | args = parser.parse_args() |
90 | 43 | main(args) | 46 | main(args) |
91 | diff --git a/src/layer.yaml b/src/layer.yaml | |||
92 | index 6e51d38..3ddcaa7 100644 | |||
93 | --- a/src/layer.yaml | |||
94 | +++ b/src/layer.yaml | |||
95 | @@ -1,4 +1,11 @@ | |||
97 | 1 | includes: ['layer:basic', 'layer:snap', 'interface:nrpe-external-master', 'interface:grafana-source', 'interface:http', 'interface:grafana-dashboard'] | 1 | includes: |
98 | 2 | - 'layer:basic' | ||
99 | 3 | - 'layer:snap' | ||
100 | 4 | - 'layer:tls-client' | ||
101 | 5 | - 'interface:nrpe-external-master' | ||
102 | 6 | - 'interface:grafana-source' | ||
103 | 7 | - 'interface:http' | ||
104 | 8 | - 'interface:grafana-dashboard' | ||
105 | 2 | ignore: ['.*.swp' ] | 9 | ignore: ['.*.swp' ] |
106 | 3 | options: | 10 | options: |
107 | 4 | basic: | 11 | basic: |
108 | diff --git a/src/lib/charms/layer/grafana.py b/src/lib/charms/layer/grafana.py | |||
109 | index d8fc7b8..0acb980 100644 | |||
110 | --- a/src/lib/charms/layer/grafana.py | |||
111 | +++ b/src/lib/charms/layer/grafana.py | |||
112 | @@ -15,10 +15,19 @@ from charmhelpers.core.hookenv import ( | |||
113 | 15 | ) | 15 | ) |
114 | 16 | 16 | ||
115 | 17 | from charms.layer import snap | 17 | from charms.layer import snap |
116 | 18 | from charms.reactive.flags import is_flag_set | ||
117 | 18 | 19 | ||
118 | 19 | import requests | 20 | import requests |
119 | 20 | 21 | ||
120 | 21 | 22 | ||
121 | 23 | # When using 'certifi' from the virtualenv, the system-wide certificates store | ||
122 | 24 | # is not used, so installed certificates won't be used to validate hosts. | ||
123 | 25 | # Adding the system CA bundle | ||
124 | 26 | # https://git.launchpad.net/ubuntu/+source/python-certifi/tree/debian/patches/0001-Use-Debian-provided-etc-ssl-certs-ca-certificates.cr.patch | ||
125 | 27 | SYSTEM_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt" | ||
126 | 28 | CA_CERT_PATH = "/var/snap/grafana/common/ssl/ca-certificates.crt" | ||
127 | 29 | |||
128 | 30 | |||
129 | 22 | class ChangeStatus(enum.Enum): | 31 | class ChangeStatus(enum.Enum): |
130 | 23 | """Model Snap channel states.""" | 32 | """Model Snap channel states.""" |
131 | 24 | 33 | ||
132 | @@ -42,6 +51,22 @@ def get_admin_password(): | |||
133 | 42 | return config("admin_password") or unitdata.kv().get("grafana.admin_password") | 51 | return config("admin_password") or unitdata.kv().get("grafana.admin_password") |
134 | 43 | 52 | ||
135 | 44 | 53 | ||
136 | 54 | def get_protocol(): | ||
137 | 55 | """Check if the SSL certificates were configured and return http or https.""" | ||
138 | 56 | if is_flag_set("grafana.certificates.configured"): | ||
139 | 57 | return "https" | ||
140 | 58 | else: | ||
141 | 59 | return "http" | ||
142 | 60 | |||
143 | 61 | |||
144 | 62 | def get_ca_cert_path(): | ||
145 | 63 | """Return the path where the CA certificates store is.""" | ||
146 | 64 | if config("install_method") == "snap": | ||
147 | 65 | return CA_CERT_PATH | ||
148 | 66 | else: | ||
149 | 67 | return SYSTEM_CA_BUNDLE | ||
150 | 68 | |||
151 | 69 | |||
152 | 45 | def compute_dash_title(title, remote_app=None): | 70 | def compute_dash_title(title, remote_app=None): |
153 | 46 | """Compute title for dashboards. | 71 | """Compute title for dashboards. |
154 | 47 | 72 | ||
155 | @@ -80,9 +105,10 @@ def compute_dash_title(title, remote_app=None): | |||
156 | 80 | def get_folders(): | 105 | def get_folders(): |
157 | 81 | """Retrieve all folders.""" | 106 | """Retrieve all folders.""" |
158 | 82 | r = requests.get( | 107 | r = requests.get( |
160 | 83 | "http://localhost:{}/api/folders".format(config("port")), | 108 | "{}://localhost:{}/api/folders".format(get_protocol(), config("port")), |
161 | 84 | auth=("admin", get_admin_password()), | 109 | auth=("admin", get_admin_password()), |
162 | 85 | headers={"Accept": "application/json"}, | 110 | headers={"Accept": "application/json"}, |
163 | 111 | verify=get_ca_cert_path(), | ||
164 | 86 | ) | 112 | ) |
165 | 87 | r.raise_for_status() | 113 | r.raise_for_status() |
166 | 88 | folders = r.json() | 114 | folders = r.json() |
167 | @@ -93,10 +119,11 @@ def get_folders(): | |||
168 | 93 | def create_folder(folder_name): | 119 | def create_folder(folder_name): |
169 | 94 | """Create a folder through Grafana API.""" | 120 | """Create a folder through Grafana API.""" |
170 | 95 | r = requests.post( | 121 | r = requests.post( |
172 | 96 | "http://localhost:{}/api/folders".format(config("port")), | 122 | "{}://localhost:{}/api/folders".format(get_protocol(), config("port")), |
173 | 97 | auth=("admin", get_admin_password()), | 123 | auth=("admin", get_admin_password()), |
174 | 98 | headers={"Accept": "application/json"}, | 124 | headers={"Accept": "application/json"}, |
175 | 99 | data={"title": folder_name}, | 125 | data={"title": folder_name}, |
176 | 126 | verify=get_ca_cert_path(), | ||
177 | 100 | ) | 127 | ) |
178 | 101 | r.raise_for_status() | 128 | r.raise_for_status() |
179 | 102 | folder = r.json() | 129 | folder = r.json() |
180 | @@ -129,7 +156,9 @@ def ensure_and_get_dash_folder(remote_model): | |||
181 | 129 | def post_dashboard(name, dashboard): | 156 | def post_dashboard(name, dashboard): |
182 | 130 | """Upload a dashboard to Grafana.""" | 157 | """Upload a dashboard to Grafana.""" |
183 | 131 | headers = {"Content-Type": "application/json"} | 158 | headers = {"Content-Type": "application/json"} |
185 | 132 | import_url = "http://localhost:{}/api/dashboards/db".format(config("port")) | 159 | import_url = "{}://localhost:{}/api/dashboards/db".format( |
186 | 160 | get_protocol(), config("port") | ||
187 | 161 | ) | ||
188 | 133 | passwd = get_admin_password() | 162 | passwd = get_admin_password() |
189 | 134 | if passwd is None: | 163 | if passwd is None: |
190 | 135 | return (False, "Unable to retrieve grafana password.") | 164 | return (False, "Unable to retrieve grafana password.") |
191 | @@ -139,6 +168,7 @@ def post_dashboard(name, dashboard): | |||
192 | 139 | auth=api_auth, | 168 | auth=api_auth, |
193 | 140 | headers=headers, | 169 | headers=headers, |
194 | 141 | data=json.dumps(dashboard), | 170 | data=json.dumps(dashboard), |
195 | 171 | verify=get_ca_cert_path(), | ||
196 | 142 | ) | 172 | ) |
197 | 143 | if r.status_code == 200: | 173 | if r.status_code == 200: |
198 | 144 | return (True, None) | 174 | return (True, None) |
199 | diff --git a/src/reactive/grafana.py b/src/reactive/grafana.py | |||
200 | index 37958d3..10a1656 100644 | |||
201 | --- a/src/reactive/grafana.py | |||
202 | +++ b/src/reactive/grafana.py | |||
203 | @@ -68,10 +68,13 @@ wipe_nrpe_checks (no nrpe-external-master.available) | |||
204 | 68 | import base64 | 68 | import base64 |
205 | 69 | import datetime | 69 | import datetime |
206 | 70 | import glob | 70 | import glob |
207 | 71 | import grp | ||
208 | 71 | import json | 72 | import json |
209 | 72 | import os | 73 | import os |
210 | 74 | import pwd | ||
211 | 73 | import re | 75 | import re |
212 | 74 | import shutil | 76 | import shutil |
213 | 77 | import socket | ||
214 | 75 | import subprocess | 78 | import subprocess |
215 | 76 | import time | 79 | import time |
216 | 77 | 80 | ||
217 | @@ -80,18 +83,23 @@ from charmhelpers.contrib.charmsupport import nrpe | |||
218 | 80 | from charmhelpers.core import ( | 83 | from charmhelpers.core import ( |
219 | 81 | hookenv, | 84 | hookenv, |
220 | 82 | host, | 85 | host, |
221 | 86 | templating, | ||
222 | 83 | unitdata, | 87 | unitdata, |
223 | 84 | ) | 88 | ) |
224 | 89 | from charmhelpers.core.decorators import retry_on_exception | ||
225 | 85 | from charmhelpers.core.templating import render | 90 | from charmhelpers.core.templating import render |
226 | 86 | 91 | ||
228 | 87 | from charms.layer import snap | 92 | from charms.layer import snap, tls_client |
229 | 88 | from charms.layer.grafana import ( | 93 | from charms.layer.grafana import ( |
230 | 94 | CA_CERT_PATH, | ||
231 | 89 | ChangeStatus, | 95 | ChangeStatus, |
232 | 90 | check_snap_channel, | 96 | check_snap_channel, |
233 | 91 | download_file, | 97 | download_file, |
234 | 92 | get_admin_password, | 98 | get_admin_password, |
235 | 99 | get_ca_cert_path, | ||
236 | 93 | get_deb_package_version, | 100 | get_deb_package_version, |
237 | 94 | get_installed_package_version, | 101 | get_installed_package_version, |
238 | 102 | get_protocol, | ||
239 | 95 | import_dashboard, | 103 | import_dashboard, |
240 | 96 | ) | 104 | ) |
241 | 97 | from charms.reactive import ( | 105 | from charms.reactive import ( |
242 | @@ -102,6 +110,7 @@ from charms.reactive import ( | |||
243 | 102 | when_any, | 110 | when_any, |
244 | 103 | when_not, | 111 | when_not, |
245 | 104 | ) | 112 | ) |
246 | 113 | from charms.reactive.flags import is_flag_set | ||
247 | 105 | from charms.reactive.helpers import ( | 114 | from charms.reactive.helpers import ( |
248 | 106 | any_file_changed, | 115 | any_file_changed, |
249 | 107 | is_state, | 116 | is_state, |
250 | @@ -119,8 +128,16 @@ import six | |||
251 | 119 | SVCNAME = {"snap": "snap.grafana.grafana", "apt": "grafana-server"} | 128 | SVCNAME = {"snap": "snap.grafana.grafana", "apt": "grafana-server"} |
252 | 120 | SNAP_NAME = "grafana" | 129 | SNAP_NAME = "grafana" |
253 | 121 | SNAP_DATA = "/var/snap/{}/current".format(SNAP_NAME) | 130 | SNAP_DATA = "/var/snap/{}/current".format(SNAP_NAME) |
256 | 122 | SNAP_COMMON = "/var/snap/{}/common/data".format(SNAP_NAME) | 131 | SNAP_COMMON_DIR = "/var/snap/{}/common".format(SNAP_NAME) |
257 | 123 | 132 | SNAP_COMMON_DATA = "{}/data".format(SNAP_COMMON_DIR) | |
258 | 133 | CERT_PATH = { | ||
259 | 134 | "snap": "{}/ssl/server.crt".format(SNAP_COMMON_DIR), | ||
260 | 135 | "apt": "/etc/grafana/ssl/server.crt", | ||
261 | 136 | } | ||
262 | 137 | CERT_KEY_PATH = { | ||
263 | 138 | "snap": "{}/ssl/server.key".format(SNAP_COMMON_DIR), | ||
264 | 139 | "apt": "/etc/grafana/ssl/server.key", | ||
265 | 140 | } | ||
266 | 124 | GRAFANA_INI = { | 141 | GRAFANA_INI = { |
267 | 125 | "snap": "{}/conf/grafana.ini".format(SNAP_DATA), | 142 | "snap": "{}/conf/grafana.ini".format(SNAP_DATA), |
268 | 126 | "apt": "/etc/grafana/grafana.ini", | 143 | "apt": "/etc/grafana/grafana.ini", |
269 | @@ -129,6 +146,7 @@ GRAFANA_INI_TMPL = "grafana.ini.j2" | |||
270 | 129 | GRAFANA_DEPS = ["libfontconfig1"] | 146 | GRAFANA_DEPS = ["libfontconfig1"] |
271 | 130 | DASHBOARDS_BACKUP_CRON = "/etc/cron.d/juju-dashboards-backup" | 147 | DASHBOARDS_BACKUP_CRON = "/etc/cron.d/juju-dashboards-backup" |
272 | 131 | DASHBOARDS_BACKUP_CRON_TMPL = "juju-dashboards-backup.j2" | 148 | DASHBOARDS_BACKUP_CRON_TMPL = "juju-dashboards-backup.j2" |
273 | 149 | CA_CERTIFICATES_HOOK = "/etc/ca-certificates/update.d/sync-grafana-snap" | ||
274 | 132 | 150 | ||
275 | 133 | 151 | ||
276 | 134 | try: | 152 | try: |
277 | @@ -233,7 +251,7 @@ def install_packages(): | |||
278 | 233 | 251 | ||
279 | 234 | def data_path(): | 252 | def data_path(): |
280 | 235 | """Retrieve data store depending on install method.""" | 253 | """Retrieve data store depending on install method.""" |
282 | 236 | data_dir = {"snap": SNAP_COMMON, "apt": "/var/lib/grafana"} | 254 | data_dir = {"snap": SNAP_COMMON_DATA, "apt": "/var/lib/grafana"} |
283 | 237 | source = get_install_source() | 255 | source = get_install_source() |
284 | 238 | if not source: | 256 | if not source: |
285 | 239 | remove_state("grafana.installed") | 257 | remove_state("grafana.installed") |
286 | @@ -460,7 +478,13 @@ def setup_grafana(): | |||
287 | 460 | grafana_ini = GRAFANA_INI[source] | 478 | grafana_ini = GRAFANA_INI[source] |
288 | 461 | hookenv.status_set("maintenance", "Configuring grafana") | 479 | hookenv.status_set("maintenance", "Configuring grafana") |
289 | 462 | config = hookenv.config() | 480 | config = hookenv.config() |
291 | 463 | settings = {"config": config} | 481 | settings = { |
292 | 482 | "config": config, | ||
293 | 483 | "protocol": get_protocol(), | ||
294 | 484 | "cert_file": CERT_PATH[config["install_method"]], | ||
295 | 485 | "cert_key": CERT_KEY_PATH[config["install_method"]], | ||
296 | 486 | } | ||
297 | 487 | |||
298 | 464 | smtp_auth = config.get("smtp_auth", False) | 488 | smtp_auth = config.get("smtp_auth", False) |
299 | 465 | if smtp_auth and len(smtp_auth.split(":")) == 2: | 489 | if smtp_auth and len(smtp_auth.split(":")) == 2: |
300 | 466 | settings["smtp_user"] = smtp_auth.split(":")[0] | 490 | settings["smtp_user"] = smtp_auth.split(":")[0] |
301 | @@ -501,6 +525,7 @@ def setup_backup_shedule(): | |||
302 | 501 | "directory": config.get("dashboards_backup_dir"), | 525 | "directory": config.get("dashboards_backup_dir"), |
303 | 502 | "port": config.get("port"), | 526 | "port": config.get("port"), |
304 | 503 | "backup_keys": " ".join(add_backup_api_keys()), | 527 | "backup_keys": " ".join(add_backup_api_keys()), |
305 | 528 | "scheme": get_protocol(), | ||
306 | 504 | } | 529 | } |
307 | 505 | render( | 530 | render( |
308 | 506 | source=DASHBOARDS_BACKUP_CRON_TMPL, | 531 | source=DASHBOARDS_BACKUP_CRON_TMPL, |
309 | @@ -536,6 +561,8 @@ def restart_grafana(): | |||
310 | 536 | hookenv.log(msg) | 561 | hookenv.log(msg) |
311 | 537 | hookenv.status_set("maintenance", msg) | 562 | hookenv.status_set("maintenance", msg) |
312 | 538 | host.service_restart(svcname) | 563 | host.service_restart(svcname) |
313 | 564 | |||
314 | 565 | _block_until_port_open() | ||
315 | 539 | hookenv.status_set("active", "Ready") | 566 | hookenv.status_set("active", "Ready") |
316 | 540 | set_state("grafana.started") | 567 | set_state("grafana.started") |
317 | 541 | hookenv.status_set("active", "Started {}".format(svcname)) | 568 | hookenv.status_set("active", "Started {}".format(svcname)) |
318 | @@ -562,11 +589,18 @@ def update_nrpe_config(): | |||
319 | 562 | nrpe.add_init_service_checks(nrpe_setup, [svcname], current_unit) | 589 | nrpe.add_init_service_checks(nrpe_setup, [svcname], current_unit) |
320 | 563 | 590 | ||
321 | 564 | # write the http check | 591 | # write the http check |
327 | 565 | nrpe_setup.add_check( | 592 | if get_protocol() == "https": |
328 | 566 | "grafana_http", | 593 | nrpe_setup.add_check( |
329 | 567 | "Grafana HTTP check", | 594 | "grafana_http", |
330 | 568 | "check_http -I 127.0.0.1 -p {} -u /login".format(config["port"]), | 595 | "Grafana HTTPS check", |
331 | 569 | ) | 596 | "check_http -S -I 127.0.0.1 -p {} -u /login".format(config["port"]), |
332 | 597 | ) | ||
333 | 598 | else: | ||
334 | 599 | nrpe_setup.add_check( | ||
335 | 600 | "grafana_http", | ||
336 | 601 | "Grafana HTTP check", | ||
337 | 602 | "check_http -I 127.0.0.1 -p {} -u /login".format(config["port"]), | ||
338 | 603 | ) | ||
339 | 570 | 604 | ||
340 | 571 | nrpe_setup.write() | 605 | nrpe_setup.write() |
341 | 572 | set_state("grafana.nagios-setup.completed") | 606 | set_state("grafana.nagios-setup.completed") |
342 | @@ -747,8 +781,9 @@ def get_current_dashboards(port, passwd): | |||
343 | 747 | https://grafana.com/docs/grafana/latest/http_api/folder_dashboard_search/ | 781 | https://grafana.com/docs/grafana/latest/http_api/folder_dashboard_search/ |
344 | 748 | """ | 782 | """ |
345 | 749 | dash_req = requests.get( | 783 | dash_req = requests.get( |
347 | 750 | "http://127.0.0.1:{}/api/search?type=dash-db".format(port), | 784 | "{}://127.0.0.1:{}/api/search?type=dash-db".format(get_protocol(), port), |
348 | 751 | auth=("admin", passwd), | 785 | auth=("admin", passwd), |
349 | 786 | verify=get_ca_cert_path(), | ||
350 | 752 | ) | 787 | ) |
351 | 753 | return dash_req.json() if dash_req.status_code == 200 else [] | 788 | return dash_req.json() if dash_req.status_code == 200 else [] |
352 | 754 | 789 | ||
353 | @@ -766,8 +801,9 @@ def get_current_dashboard_json(uid, port, passwd): | |||
354 | 766 | return default_dashboard | 801 | return default_dashboard |
355 | 767 | 802 | ||
356 | 768 | dash_req = requests.get( | 803 | dash_req = requests.get( |
358 | 769 | "http://127.0.0.1:{}/api/dashboards/uid/{}".format(port, uid), | 804 | "{}://127.0.0.1:{}/api/dashboards/uid/{}".format(get_protocol(), port, uid), |
359 | 770 | auth=("admin", passwd), | 805 | auth=("admin", passwd), |
360 | 806 | verify=get_ca_cert_path(), | ||
361 | 771 | ) | 807 | ) |
362 | 772 | if dash_req.status_code != 200: | 808 | if dash_req.status_code != 200: |
363 | 773 | return default_dashboard | 809 | return default_dashboard |
364 | @@ -856,8 +892,13 @@ def check_and_add_dashboard( | |||
365 | 856 | ) | 892 | ) |
366 | 857 | 893 | ||
367 | 858 | hookenv.log("Using Dashboard Template: {}".format(filename)) | 894 | hookenv.log("Using Dashboard Template: {}".format(filename)) |
370 | 859 | post_req = "http://127.0.0.1:{}/api/dashboards/db".format(port) | 895 | post_req = "{}://127.0.0.1:{}/api/dashboards/db".format(get_protocol(), port) |
371 | 860 | r = requests.post(post_req, json=dashboard_json, auth=("admin", gf_adminpasswd)) | 896 | r = requests.post( |
372 | 897 | post_req, | ||
373 | 898 | json=dashboard_json, | ||
374 | 899 | auth=("admin", gf_adminpasswd), | ||
375 | 900 | verify=get_ca_cert_path(), | ||
376 | 901 | ) | ||
377 | 861 | 902 | ||
378 | 862 | if r.status_code != 200: | 903 | if r.status_code != 200: |
379 | 863 | hookenv.log( | 904 | hookenv.log( |
380 | @@ -1075,8 +1116,9 @@ def get_orgs(port, passwd): | |||
381 | 1075 | https://grafana.com/docs/grafana/latest/http_api/org/ | 1116 | https://grafana.com/docs/grafana/latest/http_api/org/ |
382 | 1076 | """ | 1117 | """ |
383 | 1077 | req = requests.get( | 1118 | req = requests.get( |
385 | 1078 | "http://127.0.0.1:{}/api/orgs".format(port), | 1119 | "{}://127.0.0.1:{}/api/orgs".format(get_protocol(), port), |
386 | 1079 | auth=("admin", passwd), | 1120 | auth=("admin", passwd), |
387 | 1121 | verify=get_ca_cert_path(), | ||
388 | 1080 | ) | 1122 | ) |
389 | 1081 | return req.json() if req.status_code == 200 else [] | 1123 | return req.json() if req.status_code == 200 else [] |
390 | 1082 | 1124 | ||
391 | @@ -1255,3 +1297,83 @@ def import_dashboards(dashboards): | |||
392 | 1255 | req.respond(success, reason) | 1297 | req.respond(success, reason) |
393 | 1256 | 1298 | ||
394 | 1257 | kv.set("dash_digests", dashboard_digests) | 1299 | kv.set("dash_digests", dashboard_digests) |
395 | 1300 | |||
396 | 1301 | |||
397 | 1302 | @when("certificates.available") | ||
398 | 1303 | @when_not("grafana.certificates.configured") | ||
399 | 1304 | @when_not("grafana.certificates.requested") | ||
400 | 1305 | def tls_request_certificate(tls): | ||
401 | 1306 | """Create a server certificate for this server.""" | ||
402 | 1307 | # Use the public ip of this unit as the Common Name for the certificate. | ||
403 | 1308 | common_name = hookenv.unit_public_ip() | ||
404 | 1309 | # Get a list of Subject Alt Names for the certificate. | ||
405 | 1310 | sans = [] | ||
406 | 1311 | sans.append(hookenv.unit_public_ip()) | ||
407 | 1312 | sans.append(hookenv.unit_private_ip()) | ||
408 | 1313 | sans.append(socket.gethostname()) | ||
409 | 1314 | sans.append("127.0.0.1") | ||
410 | 1315 | sans.append("localhost") | ||
411 | 1316 | hookenv.log( | ||
412 | 1317 | "Requesting certificate with CN: {common_name}".format(common_name=common_name) | ||
413 | 1318 | ) | ||
414 | 1319 | tls_client.request_server_cert( | ||
415 | 1320 | common_name, | ||
416 | 1321 | sans, | ||
417 | 1322 | crt_path=CERT_PATH[hookenv.config("install_method")], | ||
418 | 1323 | key_path=CERT_KEY_PATH[hookenv.config("install_method")], | ||
419 | 1324 | ) | ||
420 | 1325 | _maybe_install_ca_certificates_hook() | ||
421 | 1326 | set_state("grafana.certificates.requested") | ||
422 | 1327 | |||
423 | 1328 | |||
424 | 1329 | @when("tls_client.certs.changed") | ||
425 | 1330 | @when("grafana.certificates.requested") | ||
426 | 1331 | def tls_certificate_changed(): | ||
427 | 1332 | """Request to reconfigure grafana after the certificates were written.""" | ||
428 | 1333 | # the certificates are written in owned by root and 0o770 permissions | ||
429 | 1334 | # which prevents grafana-server process to read them when using the debian | ||
430 | 1335 | # package, so the parent directory needs to allow the access by 'grafana' | ||
431 | 1336 | # group. | ||
432 | 1337 | if hookenv.config("install_method") == "apt": | ||
433 | 1338 | uid = pwd.getpwnam("root").pw_uid | ||
434 | 1339 | gid = grp.getgrnam("grafana").gr_gid | ||
435 | 1340 | ssl_dir = os.path.dirname(CERT_PATH[hookenv.config("install_method")]) | ||
436 | 1341 | os.chown(ssl_dir, uid, gid) | ||
437 | 1342 | |||
438 | 1343 | # making sure the hook to update the certificate inside the snap's common | ||
439 | 1344 | # dir is executed if needed. | ||
440 | 1345 | subprocess.check_call(["update-ca-certificates"]) | ||
441 | 1346 | |||
442 | 1347 | set_state("grafana.certificates.configured") | ||
443 | 1348 | if is_flag_set("grafana.configured"): | ||
444 | 1349 | remove_state("grafana.configured") | ||
445 | 1350 | |||
446 | 1351 | |||
447 | 1352 | def _maybe_install_ca_certificates_hook(): | ||
448 | 1353 | # When grafana is running within a snap won't have access to the system's | ||
449 | 1354 | # certificates store, so a hook script is installed to rsync it on any | ||
450 | 1355 | # execution of update-ca-certificates | ||
451 | 1356 | if hookenv.config("install_method") != "snap": | ||
452 | 1357 | # when running from the deb package there is no need to install the | ||
453 | 1358 | # hook, the ca-certificates will take care of everything for us. | ||
454 | 1359 | return | ||
455 | 1360 | |||
456 | 1361 | templating.render( | ||
457 | 1362 | "sync-grafana-snap", | ||
458 | 1363 | CA_CERTIFICATES_HOOK, | ||
459 | 1364 | context={"CA_CERT_PATH": CA_CERT_PATH}, | ||
460 | 1365 | perms=0o755, | ||
461 | 1366 | ) | ||
462 | 1367 | subprocess.check_call(["update-ca-certificates"]) | ||
463 | 1368 | |||
464 | 1369 | |||
465 | 1370 | # Block for a not so small period of time, because in hyperconverged | ||
466 | 1371 | # environments, specially while a bundle is being deployed, the amount of IOPS | ||
467 | 1372 | # in flight may prevent restarts from happening fast enough. | ||
468 | 1373 | @retry_on_exception(10, base_delay=2) | ||
469 | 1374 | def _block_until_port_open(): | ||
470 | 1375 | a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
471 | 1376 | port = hookenv.config("port") | ||
472 | 1377 | location = ("127.0.0.1", int(port)) | ||
473 | 1378 | if a_socket.connect_ex(location) != 0: | ||
474 | 1379 | raise Exception("port %s is closed" % port) | ||
475 | diff --git a/src/requirements.txt b/src/requirements.txt | |||
476 | index 8462291..236ad65 100644 | |||
477 | --- a/src/requirements.txt | |||
478 | +++ b/src/requirements.txt | |||
479 | @@ -1 +1,2 @@ | |||
480 | 1 | # Include python requirements here | 1 | # Include python requirements here |
481 | 2 | rpdb | ||
482 | diff --git a/src/templates/grafana.ini.j2 b/src/templates/grafana.ini.j2 | |||
483 | index 06278fc..77d4673 100644 | |||
484 | --- a/src/templates/grafana.ini.j2 | |||
485 | +++ b/src/templates/grafana.ini.j2 | |||
486 | @@ -8,7 +8,13 @@ instance_name = {{ config.instance_name }} | |||
487 | 8 | 8 | ||
488 | 9 | [server] | 9 | [server] |
489 | 10 | # Protocol (http or https) | 10 | # Protocol (http or https) |
491 | 11 | ;protocol = http | 11 | protocol = {{ protocol }} |
492 | 12 | |||
493 | 13 | {%- if protocol == "https" %} | ||
494 | 14 | # https certs & key file | ||
495 | 15 | cert_file = {{ cert_file }} | ||
496 | 16 | cert_key = {{ cert_key }} | ||
497 | 17 | {%- endif %} | ||
498 | 12 | 18 | ||
499 | 13 | # The ip address to bind to, empty will bind to all interfaces | 19 | # The ip address to bind to, empty will bind to all interfaces |
500 | 14 | http_addr = 0.0.0.0 | 20 | http_addr = 0.0.0.0 |
501 | diff --git a/src/templates/juju-dashboards-backup.j2 b/src/templates/juju-dashboards-backup.j2 | |||
502 | index 00f0737..993c48a 100644 | |||
503 | --- a/src/templates/juju-dashboards-backup.j2 | |||
504 | +++ b/src/templates/juju-dashboards-backup.j2 | |||
505 | @@ -1,5 +1,5 @@ | |||
506 | 1 | # | 1 | # |
507 | 2 | # Please do not edit. Juju will overwrite it. | 2 | # Please do not edit. Juju will overwrite it. |
508 | 3 | # | 3 | # |
510 | 4 | {{ schedule }} root /usr/local/bin/dashboards_backup -d {{ directory }} -p {{ port }} {{ backup_keys }} | 4 | {{ schedule }} root /usr/local/bin/dashboards_backup -d {{ directory }} -p {{ port }} -s {{ scheme }} {{ backup_keys }} |
511 | 5 | 5 | ||
512 | diff --git a/src/templates/sync-grafana-snap b/src/templates/sync-grafana-snap | |||
513 | 6 | new file mode 100644 | 6 | new file mode 100644 |
514 | index 0000000..7bf28b2 | |||
515 | --- /dev/null | |||
516 | +++ b/src/templates/sync-grafana-snap | |||
517 | @@ -0,0 +1,7 @@ | |||
518 | 1 | #!/bin/sh | ||
519 | 2 | # | ||
520 | 3 | # This file is managed by juju. | ||
521 | 4 | # | ||
522 | 5 | |||
523 | 6 | set -e | ||
524 | 7 | rsync /etc/ssl/certs/ca-certificates.crt {{ CA_CERT_PATH }} | ||
525 | diff --git a/src/tests/functional/requirements.txt b/src/tests/functional/requirements.txt | |||
526 | index bbe8435..a9af82d 100644 | |||
527 | --- a/src/tests/functional/requirements.txt | |||
528 | +++ b/src/tests/functional/requirements.txt | |||
529 | @@ -1,2 +1,8 @@ | |||
530 | 1 | git+https://github.com/openstack-charmers/zaza.git#egg=zaza | 1 | git+https://github.com/openstack-charmers/zaza.git#egg=zaza |
531 | 2 | <<<<<<< src/tests/functional/requirements.txt | ||
532 | 2 | python-openstackclient | 3 | python-openstackclient |
533 | 4 | ======= | ||
534 | 5 | # OpenStack tools current;y required when using OpenStack provider. | ||
535 | 6 | python-keystoneclient | ||
536 | 7 | python-novaclient | ||
537 | 8 | >>>>>>> src/tests/functional/requirements.txt | ||
538 | diff --git a/src/tests/functional/tests/bundles/bionic-snap-tls.yaml b/src/tests/functional/tests/bundles/bionic-snap-tls.yaml | |||
539 | 3 | new file mode 120000 | 9 | new file mode 120000 |
540 | index 0000000..f81f6ff | |||
541 | --- /dev/null | |||
542 | +++ b/src/tests/functional/tests/bundles/bionic-snap-tls.yaml | |||
543 | @@ -0,0 +1 @@ | |||
544 | 1 | base.yaml | ||
545 | 0 | \ No newline at end of file | 2 | \ No newline at end of file |
546 | diff --git a/src/tests/functional/tests/bundles/bionic-tls.yaml b/src/tests/functional/tests/bundles/bionic-tls.yaml | |||
547 | 1 | new file mode 120000 | 3 | new file mode 120000 |
548 | index 0000000..f81f6ff | |||
549 | --- /dev/null | |||
550 | +++ b/src/tests/functional/tests/bundles/bionic-tls.yaml | |||
551 | @@ -0,0 +1 @@ | |||
552 | 1 | base.yaml | ||
553 | 0 | \ No newline at end of file | 2 | \ No newline at end of file |
554 | diff --git a/src/tests/functional/tests/bundles/focal-snap-tls.yaml b/src/tests/functional/tests/bundles/focal-snap-tls.yaml | |||
555 | 1 | new file mode 120000 | 3 | new file mode 120000 |
556 | index 0000000..f81f6ff | |||
557 | --- /dev/null | |||
558 | +++ b/src/tests/functional/tests/bundles/focal-snap-tls.yaml | |||
559 | @@ -0,0 +1 @@ | |||
560 | 1 | base.yaml | ||
561 | 0 | \ No newline at end of file | 2 | \ No newline at end of file |
562 | diff --git a/src/tests/functional/tests/bundles/focal-tls.yaml b/src/tests/functional/tests/bundles/focal-tls.yaml | |||
563 | 1 | new file mode 120000 | 3 | new file mode 120000 |
564 | index 0000000..f81f6ff | |||
565 | --- /dev/null | |||
566 | +++ b/src/tests/functional/tests/bundles/focal-tls.yaml | |||
567 | @@ -0,0 +1 @@ | |||
568 | 1 | base.yaml | ||
569 | 0 | \ No newline at end of file | 2 | \ No newline at end of file |
570 | diff --git a/src/tests/functional/tests/bundles/overlays/bionic-snap-tls.yaml.j2 b/src/tests/functional/tests/bundles/overlays/bionic-snap-tls.yaml.j2 | |||
571 | 1 | new file mode 100644 | 3 | new file mode 100644 |
572 | index 0000000..e302099 | |||
573 | --- /dev/null | |||
574 | +++ b/src/tests/functional/tests/bundles/overlays/bionic-snap-tls.yaml.j2 | |||
575 | @@ -0,0 +1,11 @@ | |||
576 | 1 | series: bionic | ||
577 | 2 | applications: | ||
578 | 3 | grafana: | ||
579 | 4 | options: | ||
580 | 5 | install_method: snap | ||
581 | 6 | snap_channel: 6/stable | ||
582 | 7 | easyrsa: | ||
583 | 8 | charm: cs:~containers/easyrsa | ||
584 | 9 | num_units: 1 | ||
585 | 10 | relations: | ||
586 | 11 | - [ grafana:certificates, easyrsa ] | ||
587 | diff --git a/src/tests/functional/tests/bundles/overlays/bionic-tls.yaml.j2 b/src/tests/functional/tests/bundles/overlays/bionic-tls.yaml.j2 | |||
588 | 0 | new file mode 100644 | 12 | new file mode 100644 |
589 | index 0000000..e728037 | |||
590 | --- /dev/null | |||
591 | +++ b/src/tests/functional/tests/bundles/overlays/bionic-tls.yaml.j2 | |||
592 | @@ -0,0 +1,10 @@ | |||
593 | 1 | series: bionic | ||
594 | 2 | applications: | ||
595 | 3 | grafana: | ||
596 | 4 | options: | ||
597 | 5 | install_method: apt | ||
598 | 6 | easyrsa: | ||
599 | 7 | charm: cs:~containers/easyrsa | ||
600 | 8 | num_units: 1 | ||
601 | 9 | relations: | ||
602 | 10 | - [ grafana:certificates, easyrsa ] | ||
603 | diff --git a/src/tests/functional/tests/bundles/overlays/focal-snap-tls.yaml.j2 b/src/tests/functional/tests/bundles/overlays/focal-snap-tls.yaml.j2 | |||
604 | 0 | new file mode 100644 | 11 | new file mode 100644 |
605 | index 0000000..259f3c5 | |||
606 | --- /dev/null | |||
607 | +++ b/src/tests/functional/tests/bundles/overlays/focal-snap-tls.yaml.j2 | |||
608 | @@ -0,0 +1,32 @@ | |||
609 | 1 | series: focal | ||
610 | 2 | applications: | ||
611 | 3 | grafana: | ||
612 | 4 | num_units: 1 | ||
613 | 5 | options: | ||
614 | 6 | install_method: snap | ||
615 | 7 | snap_channel: 6/stable | ||
616 | 8 | ceph-mon: | ||
617 | 9 | series: bionic | ||
618 | 10 | ceph-osd: | ||
619 | 11 | series: bionic | ||
620 | 12 | glance: | ||
621 | 13 | series: bionic | ||
622 | 14 | keystone: | ||
623 | 15 | series: bionic | ||
624 | 16 | mysql: | ||
625 | 17 | series: bionic | ||
626 | 18 | rabbitmq-server: | ||
627 | 19 | series: bionic | ||
628 | 20 | prometheus: | ||
629 | 21 | series: bionic | ||
630 | 22 | prometheus-ceph-exporter: | ||
631 | 23 | series: bionic | ||
632 | 24 | prometheus-libvirt-exporter: | ||
633 | 25 | series: bionic | ||
634 | 26 | nagios: | ||
635 | 27 | series: bionic | ||
636 | 28 | easyrsa: | ||
637 | 29 | charm: cs:~containers/easyrsa | ||
638 | 30 | num_units: 1 | ||
639 | 31 | relations: | ||
640 | 32 | - [ grafana:certificates, easyrsa ] | ||
641 | diff --git a/src/tests/functional/tests/bundles/overlays/focal-tls.yaml.j2 b/src/tests/functional/tests/bundles/overlays/focal-tls.yaml.j2 | |||
642 | 0 | new file mode 100644 | 33 | new file mode 100644 |
643 | index 0000000..b6283dc | |||
644 | --- /dev/null | |||
645 | +++ b/src/tests/functional/tests/bundles/overlays/focal-tls.yaml.j2 | |||
646 | @@ -0,0 +1,31 @@ | |||
647 | 1 | series: focal | ||
648 | 2 | applications: | ||
649 | 3 | grafana: | ||
650 | 4 | num_units: 1 | ||
651 | 5 | options: | ||
652 | 6 | install_method: apt | ||
653 | 7 | ceph-mon: | ||
654 | 8 | series: bionic | ||
655 | 9 | ceph-osd: | ||
656 | 10 | series: bionic | ||
657 | 11 | glance: | ||
658 | 12 | series: bionic | ||
659 | 13 | keystone: | ||
660 | 14 | series: bionic | ||
661 | 15 | mysql: | ||
662 | 16 | series: bionic | ||
663 | 17 | rabbitmq-server: | ||
664 | 18 | series: bionic | ||
665 | 19 | prometheus: | ||
666 | 20 | series: bionic | ||
667 | 21 | prometheus-ceph-exporter: | ||
668 | 22 | series: bionic | ||
669 | 23 | prometheus-libvirt-exporter: | ||
670 | 24 | series: bionic | ||
671 | 25 | nagios: | ||
672 | 26 | series: bionic | ||
673 | 27 | easyrsa: | ||
674 | 28 | charm: cs:~containers/easyrsa | ||
675 | 29 | num_units: 1 | ||
676 | 30 | relations: | ||
677 | 31 | - [ grafana:certificates, easyrsa ] | ||
678 | diff --git a/src/tests/functional/tests/bundles/overlays/xenial-tls.yaml.j2 b/src/tests/functional/tests/bundles/overlays/xenial-tls.yaml.j2 | |||
679 | 0 | new file mode 100644 | 32 | new file mode 100644 |
680 | index 0000000..86f7ebf | |||
681 | --- /dev/null | |||
682 | +++ b/src/tests/functional/tests/bundles/overlays/xenial-tls.yaml.j2 | |||
683 | @@ -0,0 +1,12 @@ | |||
684 | 1 | series: xenial | ||
685 | 2 | applications: | ||
686 | 3 | grafana: | ||
687 | 4 | options: | ||
688 | 5 | install_method: apt | ||
689 | 6 | easyrsa: | ||
690 | 7 | # Use bionic version as easyrsa is failing to install on xenial | ||
691 | 8 | series: bionic | ||
692 | 9 | charm: cs:~containers/easyrsa | ||
693 | 10 | num_units: 1 | ||
694 | 11 | relations: | ||
695 | 12 | - [ grafana:certificates, easyrsa ] | ||
696 | diff --git a/src/tests/functional/tests/bundles/xenial-tls.yaml b/src/tests/functional/tests/bundles/xenial-tls.yaml | |||
697 | 0 | new file mode 120000 | 13 | new file mode 120000 |
698 | index 0000000..f81f6ff | |||
699 | --- /dev/null | |||
700 | +++ b/src/tests/functional/tests/bundles/xenial-tls.yaml | |||
701 | @@ -0,0 +1 @@ | |||
702 | 1 | base.yaml | ||
703 | 0 | \ No newline at end of file | 2 | \ No newline at end of file |
704 | diff --git a/src/tests/functional/tests/test_grafana.py b/src/tests/functional/tests/test_grafana.py | |||
705 | index d0e54d4..03fe575 100644 | |||
706 | --- a/src/tests/functional/tests/test_grafana.py | |||
707 | +++ b/src/tests/functional/tests/test_grafana.py | |||
708 | @@ -1,6 +1,8 @@ | |||
709 | 1 | """Encapsulate prometheus-openstack-exporter testing.""" | 1 | """Encapsulate prometheus-openstack-exporter testing.""" |
710 | 2 | import json | 2 | import json |
711 | 3 | import logging | 3 | import logging |
712 | 4 | import os | ||
713 | 5 | import tempfile | ||
714 | 4 | import time | 6 | import time |
715 | 5 | import unittest | 7 | import unittest |
716 | 6 | 8 | ||
717 | @@ -16,7 +18,10 @@ DEFAULT_BACKUP_DIRECTORY = "/srv/backups" | |||
718 | 16 | 18 | ||
719 | 17 | 19 | ||
720 | 18 | class BaseGrafanaTest(unittest.TestCase): | 20 | class BaseGrafanaTest(unittest.TestCase): |
722 | 19 | """Base for Prometheus-openstack-exporter charm tests.""" | 21 | """Base for Prometheus-openstack-exporter charm tests. |
723 | 22 | |||
724 | 23 | - Get the CA from easyrsa (when available) and write it on disk. | ||
725 | 24 | """ | ||
726 | 20 | 25 | ||
727 | 21 | _admin_pass = None | 26 | _admin_pass = None |
728 | 22 | 27 | ||
729 | @@ -32,11 +37,45 @@ class BaseGrafanaTest(unittest.TestCase): | |||
730 | 32 | cls.units = model.get_units(cls.application_name, model_name=cls.model_name) | 37 | cls.units = model.get_units(cls.application_name, model_name=cls.model_name) |
731 | 33 | cls.grafana_ip = model.get_app_ips(cls.application_name)[0] | 38 | cls.grafana_ip = model.get_app_ips(cls.application_name)[0] |
732 | 34 | 39 | ||
733 | 40 | # get the CA certificate from the relation between easy easyrsa and | ||
734 | 41 | # grafana. | ||
735 | 42 | if cls.get_protocol() == "https": | ||
736 | 43 | rel_id = model.get_relation_id( | ||
737 | 44 | "grafana", "easyrsa", remote_interface_name="client" | ||
738 | 45 | ) | ||
739 | 46 | easyrsa_unit = model.get_units("easyrsa")[0] | ||
740 | 47 | cmd = "relation-get -r client:{} ca {}".format( | ||
741 | 48 | rel_id, easyrsa_unit.entity_id | ||
742 | 49 | ) | ||
743 | 50 | logging.info(cmd) | ||
744 | 51 | result = model.run_on_unit(easyrsa_unit.entity_id, cmd) | ||
745 | 52 | |||
746 | 53 | with tempfile.NamedTemporaryFile(mode="w", delete=False) as f: | ||
747 | 54 | f.write(result["Stdout"]) | ||
748 | 55 | f.flush() | ||
749 | 56 | cls.ca_path = f.name | ||
750 | 57 | else: | ||
751 | 58 | cls.ca_path = None | ||
752 | 59 | |||
753 | 60 | @classmethod | ||
754 | 61 | def tearDownClass(cls): | ||
755 | 62 | """Remove the CA file that was written to disk during the setUp.""" | ||
756 | 63 | if cls.ca_path: | ||
757 | 64 | os.remove(cls.ca_path) | ||
758 | 65 | |||
759 | 35 | def get_unit_status(self, unit): | 66 | def get_unit_status(self, unit): |
760 | 36 | """Get grafana unit workload status.""" | 67 | """Get grafana unit workload status.""" |
761 | 37 | u = model.get_unit_from_name(unit) | 68 | u = model.get_unit_from_name(unit) |
762 | 38 | return u.workload_status_message | 69 | return u.workload_status_message |
763 | 39 | 70 | ||
764 | 71 | @classmethod | ||
765 | 72 | def get_protocol(cls): | ||
766 | 73 | """Get protocol configured to serve Grafana.""" | ||
767 | 74 | if model.get_relation_id("grafana", "easyrsa"): | ||
768 | 75 | return "https" | ||
769 | 76 | else: | ||
770 | 77 | return "http" | ||
771 | 78 | |||
772 | 40 | @property | 79 | @property |
773 | 41 | def admin_password(self): | 80 | def admin_password(self): |
774 | 42 | """Get grafana admin password.""" | 81 | """Get grafana admin password.""" |
775 | @@ -58,11 +97,14 @@ class BaseGrafanaTest(unittest.TestCase): | |||
776 | 58 | dashboards = [] | 97 | dashboards = [] |
777 | 59 | while True: | 98 | while True: |
778 | 60 | req = requests.get( | 99 | req = requests.get( |
780 | 61 | "http://{host}:{port}" | 100 | "{protocol}://{host}:{port}" |
781 | 62 | "/api/search?dashboardIds".format( | 101 | "/api/search?dashboardIds".format( |
783 | 63 | host=self.grafana_ip, port=DEFAULT_API_PORT | 102 | protocol=self.get_protocol(), |
784 | 103 | host=self.grafana_ip, | ||
785 | 104 | port=DEFAULT_API_PORT, | ||
786 | 64 | ), | 105 | ), |
787 | 65 | auth=("admin", self.admin_password), | 106 | auth=("admin", self.admin_password), |
788 | 107 | verify=self.ca_path, | ||
789 | 66 | ) | 108 | ) |
790 | 67 | self.assertTrue(req.status_code == 200) | 109 | self.assertTrue(req.status_code == 200) |
791 | 68 | dashboards = json.loads(req.text) | 110 | dashboards = json.loads(req.text) |
792 | @@ -113,11 +155,19 @@ class CharmOperationTest(BaseGrafanaTest): | |||
793 | 113 | Curl the api endpoint. | 155 | Curl the api endpoint. |
794 | 114 | We'll retry until the TEST_TIMEOUT. | 156 | We'll retry until the TEST_TIMEOUT. |
795 | 115 | """ | 157 | """ |
796 | 158 | if self.get_protocol() == "https": | ||
797 | 159 | # -S instructs the check_http plugin to check the SSL port which | ||
798 | 160 | # defaults to 443. | ||
799 | 161 | cmd = "/usr/lib/nagios/plugins/check_http -S" | ||
800 | 162 | else: | ||
801 | 163 | cmd = "/usr/lib/nagios/plugins/check_http" | ||
802 | 164 | |||
803 | 116 | test_command = "{} -I 127.0.0.1 -p {} -u {}".format( | 165 | test_command = "{} -I 127.0.0.1 -p {} -u {}".format( |
805 | 117 | "/usr/lib/nagios/plugins/check_http", | 166 | cmd, |
806 | 118 | DEFAULT_API_PORT, | 167 | DEFAULT_API_PORT, |
807 | 119 | DEFAULT_API_URL, | 168 | DEFAULT_API_URL, |
808 | 120 | ) | 169 | ) |
809 | 170 | logging.debug("test api ready command: %s" % test_command) | ||
810 | 121 | timeout = time.time() + TEST_TIMEOUT | 171 | timeout = time.time() + TEST_TIMEOUT |
811 | 122 | while time.time() < timeout: | 172 | while time.time() < timeout: |
812 | 123 | response = model.run_on_unit(self.lead_unit_name, test_command) | 173 | response = model.run_on_unit(self.lead_unit_name, test_command) |
813 | @@ -130,9 +180,11 @@ class CharmOperationTest(BaseGrafanaTest): | |||
814 | 130 | 180 | ||
815 | 131 | # we didn't get rc=0 in the allowed time, fail the test | 181 | # we didn't get rc=0 in the allowed time, fail the test |
816 | 132 | self.fail( | 182 | self.fail( |
818 | 133 | "http port didn't respond to the command \n" | 183 | "{protocol} port didn't respond to the command \n" |
819 | 134 | "'{test_command}' as expected.\n" | 184 | "'{test_command}' as expected.\n" |
821 | 135 | "Result: {result}".format(test_command=test_command, result=response) | 185 | "Result: {result}".format( |
822 | 186 | protocol=self.get_protocol(), test_command=test_command, result=response | ||
823 | 187 | ) | ||
824 | 136 | ) | 188 | ) |
825 | 137 | 189 | ||
826 | 138 | def test_02_nrpe_http_check(self): | 190 | def test_02_nrpe_http_check(self): |
827 | @@ -140,10 +192,19 @@ class CharmOperationTest(BaseGrafanaTest): | |||
828 | 140 | logging.debug( | 192 | logging.debug( |
829 | 141 | "Verify the nrpe check is created and has the required content..." | 193 | "Verify the nrpe check is created and has the required content..." |
830 | 142 | ) | 194 | ) |
835 | 143 | expected_nrpe_check = ( | 195 | if self.get_protocol() == "https": |
836 | 144 | "command[check_grafana_http]=/usr/lib/nagios/plugins/check_http " | 196 | expected_nrpe_check = ( |
837 | 145 | "-I 127.0.0.1 -p 3000 -u /login" | 197 | "command[check_grafana_http]=" |
838 | 146 | ) | 198 | "/usr/lib/nagios/plugins/check_http " |
839 | 199 | "-S -I 127.0.0.1 -p 3000 -u /login" | ||
840 | 200 | ) | ||
841 | 201 | else: | ||
842 | 202 | expected_nrpe_check = ( | ||
843 | 203 | "command[check_grafana_http]=" | ||
844 | 204 | "/usr/lib/nagios/plugins/check_http " | ||
845 | 205 | "-I 127.0.0.1 -p 3000 -u /login" | ||
846 | 206 | ) | ||
847 | 207 | |||
848 | 147 | cmd = "cat /etc/nagios/nrpe.d/check_grafana_http.cfg" | 208 | cmd = "cat /etc/nagios/nrpe.d/check_grafana_http.cfg" |
849 | 148 | result = model.run_on_unit(self.lead_unit_name, cmd) | 209 | result = model.run_on_unit(self.lead_unit_name, cmd) |
850 | 149 | code = result.get("Code") | 210 | code = result.get("Code") |
851 | @@ -213,10 +274,13 @@ class CharmOperationTest(BaseGrafanaTest): | |||
852 | 213 | self.assertTrue(action.data["results"]["Code"] == "0") | 274 | self.assertTrue(action.data["results"]["Code"] == "0") |
853 | 214 | time.sleep(30) # Dirty hack to overcome race condition | 275 | time.sleep(30) # Dirty hack to overcome race condition |
854 | 215 | req = requests.get( | 276 | req = requests.get( |
857 | 216 | "http://{host}:{port}/api/org/".format( | 277 | "{protocol}://{host}:{port}/api/org/".format( |
858 | 217 | host=self.grafana_ip, port=DEFAULT_API_PORT | 278 | protocol=self.get_protocol(), |
859 | 279 | host=self.grafana_ip, | ||
860 | 280 | port=DEFAULT_API_PORT, | ||
861 | 218 | ), | 281 | ), |
862 | 219 | auth=("foouser", "sikkrit"), | 282 | auth=("foouser", "sikkrit"), |
863 | 283 | verify=self.ca_path, | ||
864 | 220 | ) | 284 | ) |
865 | 221 | self.assertTrue(req.status_code == 200) | 285 | self.assertTrue(req.status_code == 200) |
866 | 222 | self.assertTrue("name" in req.json()) | 286 | self.assertTrue("name" in req.json()) |
867 | diff --git a/src/tests/functional/tests/tests.yaml b/src/tests/functional/tests/tests.yaml | |||
868 | index 909b776..736607f 100644 | |||
869 | --- a/src/tests/functional/tests/tests.yaml | |||
870 | +++ b/src/tests/functional/tests/tests.yaml | |||
871 | @@ -1,10 +1,15 @@ | |||
872 | 1 | charm_name: grafana | 1 | charm_name: grafana |
873 | 2 | gate_bundles: | 2 | gate_bundles: |
874 | 3 | - model_apt_install: focal | 3 | - model_apt_install: focal |
875 | 4 | - model_apt_install: focal-tls | ||
876 | 4 | - model_apt_install: bionic | 5 | - model_apt_install: bionic |
877 | 6 | - model_apt_install: bionic-tls | ||
878 | 5 | - model_apt_install: xenial | 7 | - model_apt_install: xenial |
879 | 8 | - model_apt_install: xenial-tls | ||
880 | 6 | - model_snap_install: bionic-snap | 9 | - model_snap_install: bionic-snap |
881 | 10 | - model_snap_install: bionic-snap-tls | ||
882 | 7 | - model_snap_install: focal-snap | 11 | - model_snap_install: focal-snap |
883 | 12 | - model_snap_install: focal-snap-tls | ||
884 | 8 | smoke_bundles: | 13 | smoke_bundles: |
885 | 9 | - model_snap_install: bionic-snap | 14 | - model_snap_install: bionic-snap |
886 | 10 | dev_bundles: | 15 | dev_bundles: |
887 | @@ -25,3 +30,5 @@ target_deploy_status: | |||
888 | 25 | workload-status-message: Monitoring | 30 | workload-status-message: Monitoring |
889 | 26 | prometheus-libvirt-exporter: | 31 | prometheus-libvirt-exporter: |
890 | 27 | workload-status-message: "Exporter installed and connected to libvirt slot" | 32 | workload-status-message: "Exporter installed and connected to libvirt slot" |
891 | 33 | easyrsa: | ||
892 | 34 | workload-status-message: Certificate Authority connected. | ||
893 | diff --git a/src/tests/unit/test_grafana.py b/src/tests/unit/test_grafana.py | |||
894 | index 663fa17..2e460ef 100644 | |||
895 | --- a/src/tests/unit/test_grafana.py | |||
896 | +++ b/src/tests/unit/test_grafana.py | |||
897 | @@ -1,4 +1,6 @@ | |||
898 | 1 | """Unit tests module.""" | 1 | """Unit tests module.""" |
899 | 2 | import os | ||
900 | 3 | import socket | ||
901 | 2 | import sys | 4 | import sys |
902 | 3 | import unittest | 5 | import unittest |
903 | 4 | from os.path import isfile | 6 | from os.path import isfile |
904 | @@ -7,6 +9,8 @@ from unittest.mock import call | |||
905 | 7 | 9 | ||
906 | 8 | from charmhelpers.core import unitdata | 10 | from charmhelpers.core import unitdata |
907 | 9 | 11 | ||
908 | 12 | tls_client_mock = mock.Mock() | ||
909 | 13 | sys.modules["charms.layer.tls_client"] = tls_client_mock | ||
910 | 10 | sys.modules["charms.layer.snap"] = mock.Mock() | 14 | sys.modules["charms.layer.snap"] = mock.Mock() |
911 | 11 | 15 | ||
912 | 12 | 16 | ||
913 | @@ -148,3 +152,47 @@ class GrafanaTestCase(unittest.TestCase): | |||
914 | 148 | "dash-name", | 152 | "dash-name", |
915 | 149 | {"dashboard": {"title": "[juju-foo-app] test-title"}, "folderId": 0}, | 153 | {"dashboard": {"title": "[juju-foo-app] test-title"}, "folderId": 0}, |
916 | 150 | ) | 154 | ) |
917 | 155 | |||
918 | 156 | @mock.patch("charmhelpers.core.hookenv.charm_dir", auto_spec=True) | ||
919 | 157 | @mock.patch("reactive.grafana.templating.render", auto_spec=True) | ||
920 | 158 | @mock.patch("reactive.grafana.hookenv.config", auto_spec=True) | ||
921 | 159 | @mock.patch("reactive.grafana.hookenv.unit_public_ip", auto_spec=True) | ||
922 | 160 | @mock.patch("reactive.grafana.hookenv.unit_private_ip", auto_spec=True) | ||
923 | 161 | @mock.patch("subprocess.check_call", auto_spec=True) | ||
924 | 162 | def test_request_certificate( | ||
925 | 163 | self, | ||
926 | 164 | mock_check_call, | ||
927 | 165 | mock_private_ip, | ||
928 | 166 | mock_public_ip, | ||
929 | 167 | mock_config, | ||
930 | 168 | mock_render, | ||
931 | 169 | mock_charm_dir, | ||
932 | 170 | ): | ||
933 | 171 | """Test request certificate.""" | ||
934 | 172 | charm_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../") | ||
935 | 173 | mock_charm_dir.return_value = charm_dir | ||
936 | 174 | |||
937 | 175 | config = {"install_method": "snap"} | ||
938 | 176 | |||
939 | 177 | def fake_config(key): | ||
940 | 178 | return config[key] | ||
941 | 179 | |||
942 | 180 | mock_config.side_effect = fake_config | ||
943 | 181 | mock_public_ip.return_value = "1.2.3.4" | ||
944 | 182 | mock_private_ip.return_value = "5.6.7.8" | ||
945 | 183 | |||
946 | 184 | mock_tls = mock.Mock() | ||
947 | 185 | grafana_reactive.tls_request_certificate(mock_tls) | ||
948 | 186 | tls_client_mock.request_server_cert.assert_called_with( | ||
949 | 187 | "1.2.3.4", | ||
950 | 188 | ["1.2.3.4", "5.6.7.8", socket.gethostname(), "127.0.0.1", "localhost"], | ||
951 | 189 | crt_path=grafana_reactive.CERT_PATH["snap"], | ||
952 | 190 | key_path=grafana_reactive.CERT_KEY_PATH["snap"], | ||
953 | 191 | ) | ||
954 | 192 | |||
955 | 193 | mock_render.assert_called_with( | ||
956 | 194 | "sync-grafana-snap", | ||
957 | 195 | grafana_reactive.CA_CERTIFICATES_HOOK, | ||
958 | 196 | context={"CA_CERT_PATH": grafana_reactive.CA_CERT_PATH}, | ||
959 | 197 | perms=0o755, | ||
960 | 198 | ) | ||
961 | diff --git a/src/tox.ini b/src/tox.ini | |||
962 | index 5845e03..72d33e7 100644 | |||
963 | --- a/src/tox.ini | |||
964 | +++ b/src/tox.ini | |||
965 | @@ -21,6 +21,7 @@ passenv = | |||
966 | 21 | NO_PROXY | 21 | NO_PROXY |
967 | 22 | SNAP_HTTP_PROXY | 22 | SNAP_HTTP_PROXY |
968 | 23 | SNAP_HTTPS_PROXY | 23 | SNAP_HTTPS_PROXY |
969 | 24 | <<<<<<< src/tox.ini | ||
970 | 24 | OS_REGION_NAME | 25 | OS_REGION_NAME |
971 | 25 | OS_AUTH_VERSION | 26 | OS_AUTH_VERSION |
972 | 26 | OS_AUTH_URL | 27 | OS_AUTH_URL |
973 | @@ -31,6 +32,9 @@ passenv = | |||
974 | 31 | OS_USER_DOMAIN_NAME | 32 | OS_USER_DOMAIN_NAME |
975 | 32 | OS_PROJECT_NAME | 33 | OS_PROJECT_NAME |
976 | 33 | OS_IDENTITY_API_VERSION | 34 | OS_IDENTITY_API_VERSION |
977 | 35 | ======= | ||
978 | 36 | OS_* | ||
979 | 37 | >>>>>>> src/tox.ini | ||
980 | 34 | 38 | ||
981 | 35 | [testenv:lint] | 39 | [testenv:lint] |
982 | 36 | commands = | 40 | commands = |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.