Merge ~xavpaice/charm-grafana:lp1947669 into charm-grafana:master

Proposed by Xav Paice
Status: Merged
Approved by: James Troup
Approved revision: ad9efd5d18b3e808d0d752e0305a16e6459c85fb
Merged at revision: 17248daa461de95560156c1fc210331c2a8c1d40
Proposed branch: ~xavpaice/charm-grafana:lp1947669
Merge into: charm-grafana:master
Diff against target: 156 lines (+78/-13)
2 files modified
src/reactive/grafana.py (+28/-13)
src/tests/unit/test_grafana.py (+50/-0)
Reviewer Review Type Date Requested Status
Alvaro Uria (community) Approve
Andrea Ieri Approve
🤖 prod-jenkaas-bootstack continuous-integration Approve
Zachary Zehring (community) Approve
BootStack Reviewers Pending
Review via email: mp+410488@code.launchpad.net

Commit message

Update permissions on grafana.db on access

    If Juju accesses the sqlite db for Grafana prior to Grafana creating it,
    the file is created with root ownership and Grafana cannot write to the
    db. This change adds a db access function with chown.

    Fixes bug LP: #1947669

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alvaro Uria (aluria) wrote :

Please find a command below about "chowning" data_dir with the "grafana" user and group. I don't recall if that would work on snap-deployed envs (the snap does not create such users, as the "apt" method does).

Revision history for this message
Alvaro Uria (aluria) :
review: Needs Information
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Approve (continuous-integration)
Revision history for this message
Xav Paice (xavpaice) wrote :

Thanks Alvaro, indeed the snap runs grafana as the root user, and there's no grafana user. I've updated the change to reflect that.

Revision history for this message
Andrea Ieri (aieri) wrote :

lgtm

review: Approve
Revision history for this message
Zachary Zehring (zzehring) wrote :

Small comment inline.

review: Needs Fixing
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Approve (continuous-integration)
Revision history for this message
Xav Paice (xavpaice) wrote :

Squashed commits, added unit test, fixed typo (not using 'path' when I'd just set it), made the chmod conditional on apt install only.

Revision history for this message
Zachary Zehring (zzehring) wrote :

Very small comment inline. Otherwise LGTM

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Approve (continuous-integration)
Revision history for this message
Zachary Zehring (zzehring) wrote :

+1

review: Approve
Revision history for this message
Andrea Ieri (aieri) wrote :

two small comments inline

review: Needs Fixing
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Approve (continuous-integration)
Revision history for this message
Andrea Ieri (aieri) wrote :

+1

review: Approve
Revision history for this message
Alvaro Uria (aluria) :
review: Approve
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 17248daa461de95560156c1fc210331c2a8c1d40

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/reactive/grafana.py b/src/reactive/grafana.py
2index 0a96d4f..9672653 100644
3--- a/src/reactive/grafana.py
4+++ b/src/reactive/grafana.py
5@@ -17,6 +17,8 @@ wipe_nrpe_checks (no nrpe-external-master.available)
6 [Helper funcs]
7 * data_path(): retrieves /var/lib/grafana or /var/snap/grafana depending on
8 install_method
9+ * db_connection(): returns a sqlite3 Connection object for the Grafana database,
10+ setting permissions and owner if appropriate
11 * check_ports(new_port): close-port (if new_port) and open-port
12 * select_query(query, params=None): helper to return rows from a query
13 * insert_query(query, params=None): helper to update the db
14@@ -382,6 +384,21 @@ def check_ports(new_port):
15 kv.set("grafana.port", new_port)
16
17
18+def db_connection():
19+ """Return a connection to the Grafana sqlite db."""
20+ data_dir = data_path()
21+ if not data_dir:
22+ return None
23+ path = "{}/grafana.db".format(data_dir)
24+ conn = sqlite3.connect(path, timeout=30)
25+ source = get_install_source()
26+ if "root" in host.owner(path) and source == "apt":
27+ # Grafana runs as 'grafana', but Juju has made the grafana.db file as root.
28+ host.chownr(data_dir, "grafana", "grafana")
29+ os.chmod(path, 0o640)
30+ return conn
31+
32+
33 def select_query(query, params=None):
34 """Return rows for a query.
35
36@@ -391,10 +408,9 @@ def select_query(query, params=None):
37 :type params: list of strings
38 :rtype: list of rows found
39 """
40- data_dir = data_path()
41- if not data_dir:
42+ conn = db_connection()
43+ if not conn:
44 return
45- conn = sqlite3.connect("{}/grafana.db".format(data_dir), timeout=30)
46 cur = conn.cursor()
47 if params:
48 return cur.execute(query, params).fetchall()
49@@ -411,10 +427,9 @@ def insert_query(query, params=None):
50 :type params: list of strings
51 :rtype: None
52 """
53- data_dir = data_path()
54- if not data_dir:
55+ conn = db_connection()
56+ if not conn:
57 return
58- conn = sqlite3.connect("{}/grafana.db".format(data_dir), timeout=30)
59 cur = conn.cursor()
60 if params:
61 cur.execute(query, params)
62@@ -704,7 +719,9 @@ def sources_gone(relation):
63 hookenv.log(ds)
64
65 if ds:
66- conn = sqlite3.connect("{}/grafana.db".format(data_dir), timeout=30)
67+ conn = db_connection()
68+ if not conn:
69+ return
70 cur = conn.cursor()
71 cur.execute(
72 "DELETE FROM DATA_SOURCE WHERE type=? AND url=?",
73@@ -769,10 +786,9 @@ def check_datasource(ds):
74 # 'username': 'username,
75 # 'password': 'password
76 # }
77- data_dir = data_path()
78- if not data_dir:
79+ conn = db_connection()
80+ if not conn:
81 return
82- conn = sqlite3.connect("{}/grafana.db".format(data_dir), timeout=30)
83 cur = conn.cursor()
84 query = cur.execute("SELECT id, type, name, url, is_default FROM DATA_SOURCE")
85 rows = query.fetchall()
86@@ -1223,10 +1239,9 @@ def check_adminuser():
87 stmt += ", password=?, theme='light'"
88 stmt += " WHERE id = ?"
89
90- data_dir = data_path()
91- if not data_dir:
92+ conn = db_connection()
93+ if not conn:
94 return
95- conn = sqlite3.connect("{}/grafana.db".format(data_dir), timeout=30)
96 cur = conn.cursor()
97 # Check if we have more than one user in the db
98 for x in range(0, 5):
99diff --git a/src/tests/unit/test_grafana.py b/src/tests/unit/test_grafana.py
100index 7f4a53b..f15c9e9 100644
101--- a/src/tests/unit/test_grafana.py
102+++ b/src/tests/unit/test_grafana.py
103@@ -321,3 +321,53 @@ class GrafanaTestCase(unittest.TestCase):
104 "group": group,
105 },
106 )
107+
108+ @mock.patch("sqlite3.connect")
109+ @mock.patch("os.chmod")
110+ @mock.patch("charmhelpers.core.host.chownr")
111+ @mock.patch.object(grafana_reactive, "data_path", auto_spec=True)
112+ @mock.patch("charmhelpers.core.host.owner")
113+ @mock.patch.object(grafana_reactive, "get_install_source", auto_spec=True)
114+ def test_db_connection_snap(
115+ self,
116+ mock_install_source,
117+ mock_owner,
118+ mock_data_path,
119+ mock_chownr,
120+ mock_chmod,
121+ mock_sqlite3,
122+ ):
123+ """Test that the db_connection leaves perms on grafana.db."""
124+ mock_install_source.return_value = "snap"
125+ mock_data_path.return_value = "/var/snap/grafana"
126+ mock_owner.return_value = "root"
127+ mock_sqlite3.return_value = True
128+ connection = grafana_reactive.db_connection()
129+ self.assertTrue(connection)
130+ mock_chmod.assert_not_called()
131+ mock_chownr.assert_not_called()
132+
133+ @mock.patch("sqlite3.connect")
134+ @mock.patch("os.chmod")
135+ @mock.patch("charmhelpers.core.host.chownr")
136+ @mock.patch.object(grafana_reactive, "data_path", auto_spec=True)
137+ @mock.patch("charmhelpers.core.host.owner")
138+ @mock.patch.object(grafana_reactive, "get_install_source", auto_spec=True)
139+ def test_db_connection_apt(
140+ self,
141+ mock_install_source,
142+ mock_owner,
143+ mock_data_path,
144+ mock_chownr,
145+ mock_chmod,
146+ mock_sqlite3,
147+ ):
148+ """Test that the db_connection fixes perms on grafana.db."""
149+ mock_install_source.return_value = "apt"
150+ mock_data_path.return_value = "/var/apt/grafana"
151+ mock_owner.return_value = "root"
152+ mock_sqlite3.return_value = True
153+ connection = grafana_reactive.db_connection()
154+ self.assertTrue(connection)
155+ mock_chmod.assert_called()
156+ mock_chownr.assert_called()

Subscribers

People subscribed via source and target branches

to all changes: