moat-kv 0.71.0__py3-none-any.whl → 0.71.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- moat/kv/__init__.py +6 -7
- moat/kv/_cfg.yaml +3 -2
- moat/kv/actor/__init__.py +2 -1
- moat/kv/actor/deletor.py +4 -1
- moat/kv/auth/__init__.py +12 -13
- moat/kv/auth/_test.py +4 -1
- moat/kv/auth/password.py +11 -7
- moat/kv/backend/mqtt.py +4 -5
- moat/kv/client.py +20 -39
- moat/kv/code.py +3 -3
- moat/kv/command/data.py +4 -3
- moat/kv/command/dump/__init__.py +36 -34
- moat/kv/command/internal.py +2 -3
- moat/kv/command/job.py +1 -2
- moat/kv/command/type.py +3 -6
- moat/kv/data.py +9 -8
- moat/kv/errors.py +16 -8
- moat/kv/mock/__init__.py +2 -12
- moat/kv/model.py +29 -33
- moat/kv/obj/__init__.py +3 -3
- moat/kv/obj/command.py +3 -3
- moat/kv/runner.py +4 -5
- moat/kv/server.py +106 -126
- moat/kv/types.py +10 -12
- {moat_kv-0.71.0.dist-info → moat_kv-0.71.7.dist-info}/METADATA +6 -2
- moat_kv-0.71.7.dist-info/RECORD +47 -0
- {moat_kv-0.71.0.dist-info → moat_kv-0.71.7.dist-info}/WHEEL +1 -1
- moat_kv-0.71.7.dist-info/licenses/LICENSE +3 -0
- moat_kv-0.71.7.dist-info/licenses/LICENSE.APACHE2 +202 -0
- moat_kv-0.71.7.dist-info/licenses/LICENSE.MIT +20 -0
- moat_kv-0.71.7.dist-info/top_level.txt +1 -0
- build/lib/docs/source/conf.py +0 -201
- build/lib/examples/pathify.py +0 -45
- build/lib/moat/kv/__init__.py +0 -19
- build/lib/moat/kv/_cfg.yaml +0 -93
- build/lib/moat/kv/_main.py +0 -91
- build/lib/moat/kv/actor/__init__.py +0 -98
- build/lib/moat/kv/actor/deletor.py +0 -139
- build/lib/moat/kv/auth/__init__.py +0 -444
- build/lib/moat/kv/auth/_test.py +0 -166
- build/lib/moat/kv/auth/password.py +0 -234
- build/lib/moat/kv/auth/root.py +0 -58
- build/lib/moat/kv/backend/__init__.py +0 -67
- build/lib/moat/kv/backend/mqtt.py +0 -71
- build/lib/moat/kv/client.py +0 -1025
- build/lib/moat/kv/code.py +0 -236
- build/lib/moat/kv/codec.py +0 -11
- build/lib/moat/kv/command/__init__.py +0 -1
- build/lib/moat/kv/command/acl.py +0 -180
- build/lib/moat/kv/command/auth.py +0 -261
- build/lib/moat/kv/command/code.py +0 -293
- build/lib/moat/kv/command/codec.py +0 -186
- build/lib/moat/kv/command/data.py +0 -265
- build/lib/moat/kv/command/dump/__init__.py +0 -143
- build/lib/moat/kv/command/error.py +0 -149
- build/lib/moat/kv/command/internal.py +0 -248
- build/lib/moat/kv/command/job.py +0 -433
- build/lib/moat/kv/command/log.py +0 -53
- build/lib/moat/kv/command/server.py +0 -114
- build/lib/moat/kv/command/type.py +0 -201
- build/lib/moat/kv/config.py +0 -46
- build/lib/moat/kv/data.py +0 -216
- build/lib/moat/kv/errors.py +0 -561
- build/lib/moat/kv/exceptions.py +0 -126
- build/lib/moat/kv/mock/__init__.py +0 -101
- build/lib/moat/kv/mock/mqtt.py +0 -159
- build/lib/moat/kv/mock/tracer.py +0 -63
- build/lib/moat/kv/model.py +0 -1069
- build/lib/moat/kv/obj/__init__.py +0 -646
- build/lib/moat/kv/obj/command.py +0 -241
- build/lib/moat/kv/runner.py +0 -1347
- build/lib/moat/kv/server.py +0 -2809
- build/lib/moat/kv/types.py +0 -513
- ci/rtd-requirements.txt +0 -4
- ci/test-requirements.txt +0 -7
- ci/travis.sh +0 -96
- debian/.gitignore +0 -7
- debian/changelog +0 -1435
- debian/control +0 -43
- debian/moat-kv/usr/lib/python3/dist-packages/docs/source/conf.py +0 -201
- debian/moat-kv/usr/lib/python3/dist-packages/examples/pathify.py +0 -45
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/__init__.py +0 -19
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_cfg.yaml +0 -93
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_main.py +0 -91
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/__init__.py +0 -98
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/deletor.py +0 -139
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/__init__.py +0 -444
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/_test.py +0 -166
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/password.py +0 -234
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/root.py +0 -58
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/__init__.py +0 -67
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/mqtt.py +0 -71
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/client.py +0 -1025
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/code.py +0 -236
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/codec.py +0 -11
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/__init__.py +0 -1
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/acl.py +0 -180
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/auth.py +0 -261
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/code.py +0 -293
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/codec.py +0 -186
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/data.py +0 -265
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/dump/__init__.py +0 -143
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/error.py +0 -149
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/internal.py +0 -248
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/job.py +0 -433
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/log.py +0 -53
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/server.py +0 -114
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/type.py +0 -201
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/config.py +0 -46
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/data.py +0 -216
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/errors.py +0 -561
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/exceptions.py +0 -126
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/__init__.py +0 -101
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/mqtt.py +0 -159
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/tracer.py +0 -63
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/model.py +0 -1069
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/__init__.py +0 -646
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/command.py +0 -241
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/runner.py +0 -1347
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/server.py +0 -2809
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/types.py +0 -513
- debian/moat-kv.postinst +0 -3
- debian/rules +0 -20
- debian/source/format +0 -1
- debian/watch +0 -4
- docs/Makefile +0 -20
- docs/make.bat +0 -36
- docs/source/TODO.rst +0 -61
- docs/source/_static/.gitkeep +0 -0
- docs/source/acls.rst +0 -80
- docs/source/auth.rst +0 -84
- docs/source/client_protocol.rst +0 -456
- docs/source/code.rst +0 -341
- docs/source/command_line.rst +0 -1187
- docs/source/common_protocol.rst +0 -47
- docs/source/conf.py +0 -201
- docs/source/debugging.rst +0 -70
- docs/source/extend.rst +0 -37
- docs/source/history.rst +0 -36
- docs/source/index.rst +0 -75
- docs/source/model.rst +0 -54
- docs/source/overview.rst +0 -83
- docs/source/related.rst +0 -89
- docs/source/server_protocol.rst +0 -450
- docs/source/startup.rst +0 -31
- docs/source/translator.rst +0 -244
- docs/source/tutorial.rst +0 -711
- docs/source/v3.rst +0 -168
- examples/code/transform.scale.yml +0 -21
- examples/code/transform.switch.yml +0 -82
- examples/code/transform.timeslot.yml +0 -63
- examples/pathify.py +0 -45
- moat/kv/codec.py +0 -11
- moat_kv-0.71.0.dist-info/RECORD +0 -188
- moat_kv-0.71.0.dist-info/top_level.txt +0 -9
- scripts/current +0 -15
- scripts/env +0 -8
- scripts/init +0 -39
- scripts/recover +0 -17
- scripts/rotate +0 -33
- scripts/run +0 -29
- scripts/run-all +0 -10
- scripts/run-any +0 -10
- scripts/run-single +0 -15
- scripts/success +0 -4
- systemd/moat-kv-recover.service +0 -21
- systemd/moat-kv-rotate.service +0 -20
- systemd/moat-kv-rotate.timer +0 -10
- systemd/moat-kv-run-all.service +0 -26
- systemd/moat-kv-run-all@.service +0 -25
- systemd/moat-kv-run-any.service +0 -26
- systemd/moat-kv-run-any@.service +0 -25
- systemd/moat-kv-run-single.service +0 -26
- systemd/moat-kv-run-single@.service +0 -25
- systemd/moat-kv.service +0 -27
- systemd/postinst +0 -7
- systemd/sysusers +0 -3
- {moat_kv-0.71.0.dist-info → moat_kv-0.71.7.dist-info}/licenses/LICENSE.txt +0 -0
docs/source/tutorial.rst
DELETED
@@ -1,711 +0,0 @@
|
|
1
|
-
===================
|
2
|
-
The MoaT-KV tutorial
|
3
|
-
===================
|
4
|
-
|
5
|
-
Installation
|
6
|
-
============
|
7
|
-
|
8
|
-
This part is easy. ``pip install moat-kv``.
|
9
|
-
|
10
|
-
You now have, or should have, a ``moat`` command-line utility. If not,
|
11
|
-
use this script::
|
12
|
-
|
13
|
-
#!/usr/bin/env python3
|
14
|
-
|
15
|
-
import sys
|
16
|
-
import moat.__main__
|
17
|
-
|
18
|
-
|
19
|
-
You also need a running `MQTT <https://mqtt.org>` message broker.
|
20
|
-
|
21
|
-
Start the server
|
22
|
-
================
|
23
|
-
|
24
|
-
You start an initial server with this command::
|
25
|
-
|
26
|
-
$ moat kv server -i Testing $(hostname)
|
27
|
-
Running.
|
28
|
-
|
29
|
-
By default, your MoaT-KV server will talk to the local MQTT process.
|
30
|
-
You can configure the destination by adapting the config file::
|
31
|
-
|
32
|
-
$ moat kv -C kv.server.mqtt.uri=mqtt://your-server:1883 server -i Testing $(hostname)
|
33
|
-
|
34
|
-
You can now retrieve the root value::
|
35
|
-
|
36
|
-
$ moat kv data :
|
37
|
-
"Testing"
|
38
|
-
$
|
39
|
-
|
40
|
-
As the purpose of MoaT-KV is to be a *distributed* key-value storage,
|
41
|
-
you can start another server on a different host::
|
42
|
-
|
43
|
-
two $ moat kv -C server.mqtt.uri=mqtt://your-server:1883 server $(hostname)
|
44
|
-
Running.
|
45
|
-
|
46
|
-
|
47
|
-
This will take a few seconds for the servers to sync up with each other.
|
48
|
-
You can verify that the second server has successfully synced up::
|
49
|
-
|
50
|
-
two $ moat kv data :
|
51
|
-
"Testing"
|
52
|
-
two $
|
53
|
-
|
54
|
-
The root value is not special; by convention, it contains some data about the current
|
55
|
-
MoaT-KV network.
|
56
|
-
|
57
|
-
You can now kill the first server and restart it::
|
58
|
-
|
59
|
-
$ moat kv server $(hostname)
|
60
|
-
Running.
|
61
|
-
|
62
|
-
You must **never** start a server with the ``-i`` option unless you're
|
63
|
-
creating a new and separate MoaT-KV network.
|
64
|
-
|
65
|
-
You can create separate networks by changing the ``server.root`` config
|
66
|
-
variable. Such networks do not collide with each other, other than sharing
|
67
|
-
Serf gossip bandwidth.
|
68
|
-
|
69
|
-
|
70
|
-
Data commands
|
71
|
-
=============
|
72
|
-
|
73
|
-
You might want to add an alias for "moat kv data" so that you don't
|
74
|
-
have to type so much. In ``bash``::
|
75
|
-
|
76
|
-
$ mkd() { moat kv data "$@"; }
|
77
|
-
|
78
|
-
Then, you can store arbitrary data at random MoaT-KV nodes::
|
79
|
-
|
80
|
-
$ mkd one.two.three set -e : 123
|
81
|
-
$ mkd one.two.three.four set -e : 1234
|
82
|
-
$ mkd one.two.three.four.five set -v one XXX -e two 2
|
83
|
-
$ mkd one.two.three
|
84
|
-
123
|
85
|
-
$ mkd one.two.three.four.five
|
86
|
-
one: XXX
|
87
|
-
two: 2
|
88
|
-
$
|
89
|
-
|
90
|
-
The ``-e`` flag tells the ``set`` command to evaluate the given data as a
|
91
|
-
Python expression. You can store numbers, True/False/None, binary and
|
92
|
-
Unicode strings, and lists/tuples/hashes composed of these.
|
93
|
-
|
94
|
-
Stored values may be data structures, and you can selectively change them::
|
95
|
-
|
96
|
-
$ mkd one.two.three.four.five set -e one 1
|
97
|
-
$ mkd one.two.three.four.five
|
98
|
-
one: 1
|
99
|
-
two: 2
|
100
|
-
|
101
|
-
The colon we used after ``-e`` is the empty path. More about paths below.
|
102
|
-
|
103
|
-
All entries' values are independent. MoaT-KV's storage is organized
|
104
|
-
hierarchically, (among other reasons) for ease of retrieval::
|
105
|
-
|
106
|
-
$ mkd one get -rd_
|
107
|
-
two:
|
108
|
-
three:
|
109
|
-
_: 123
|
110
|
-
four:
|
111
|
-
_: 1234
|
112
|
-
five:
|
113
|
-
_:
|
114
|
-
one: 1
|
115
|
-
two: 2
|
116
|
-
$
|
117
|
-
|
118
|
-
MoaT-KV also stores some internal data, under a special ``null`` root key.
|
119
|
-
You can use ``moat kv internal dump :`` to display them.
|
120
|
-
|
121
|
-
Path specification
|
122
|
-
------------------
|
123
|
-
|
124
|
-
MoaT-KV uses "paths" to access entries (and the partial values in them).
|
125
|
-
We chose the dot as a path separator because it's more visually distinctive
|
126
|
-
than a slash.
|
127
|
-
|
128
|
-
In MoaT-KV, paths elements are not limited to strings; integers can
|
129
|
-
also be path elements, as can ``True``, ``False``, ``None``, and tuples
|
130
|
-
composed from them. We use colons instead of dots to mark those.
|
131
|
-
The colon is also used as an escape characters for path elements that
|
132
|
-
contain dots or colons; it is easy to type and doesn't occur often,
|
133
|
-
while the traditional Unix escape character (backslash ``\\``) is
|
134
|
-
hard to type in some locales and must be duplicated almost everywhere you
|
135
|
-
want to actually use it.
|
136
|
-
|
137
|
-
A space is encoded as ``:_``. While a literal space is not a problem, it
|
138
|
-
needs to be escaped on the command line. Experience shows that people tend
|
139
|
-
to forget that. A "real" underscore ``_`` is not escaped.
|
140
|
-
|
141
|
-
There's also the empty path (i.e. the top of MoaT-KV's entry hierarchy,
|
142
|
-
not the same as a path that consists of an empty-string element!) which is
|
143
|
-
coded as a stand-alone ``:`` for much the same reason.
|
144
|
-
|
145
|
-
Anything else that follows a colon is evaluated as a Python expression.
|
146
|
-
|
147
|
-
Thus:
|
148
|
-
|
149
|
-
==== ==========
|
150
|
-
Code Meaning
|
151
|
-
---- ----------
|
152
|
-
:. literal ``.``
|
153
|
-
:: literal ``:``
|
154
|
-
:_ space
|
155
|
-
==== ==========
|
156
|
-
:t True
|
157
|
-
:f False
|
158
|
-
:n None
|
159
|
-
:e empty string
|
160
|
-
:x hex integer
|
161
|
-
:b binary integer
|
162
|
-
:y hex bytestring
|
163
|
-
:v literal bytestring
|
164
|
-
:XX eval(XX)
|
165
|
-
|
166
|
-
==== ==========
|
167
|
-
|
168
|
-
The first three are inline escape sequences while the others start a new
|
169
|
-
element.
|
170
|
-
|
171
|
-
Hex number input is purely a convenience; integers in paths are always
|
172
|
-
printed in decimal form. While you also could use ``:0x…`` in place of
|
173
|
-
``:x…``, the latter reduces visual clutter:
|
174
|
-
|
175
|
-
.. warning::
|
176
|
-
|
177
|
-
Yes, MoaT-KV supports tuples as part of paths. You probably should not use
|
178
|
-
this feature without a very good reason. "My key consists of three
|
179
|
-
random integers and I want to avoid the overhead of storing a lot of
|
180
|
-
intermediate entries" would be an example of a good reason.
|
181
|
-
|
182
|
-
MoaT-KV also allows you to use both ``False``, an integer zero, and a
|
183
|
-
floating-point zero as path elements. This is dangerous because Python's
|
184
|
-
comparison and hashing operators treat them as being equal. (Same for
|
185
|
-
``True`` and 1; same for floating point numbers without fractions and
|
186
|
-
the corresponding integers.)
|
187
|
-
|
188
|
-
Floating point numbers are also dangerous for a different reason: floats
|
189
|
-
that are not a fractional power of two, such as 1/3, are inexact.
|
190
|
-
Thus you might end up with five different entries for what was meant to
|
191
|
-
be ``1/3``.
|
192
|
-
|
193
|
-
Bottom line:
|
194
|
-
|
195
|
-
* If you do need paths elements with sub-integer numbers, consider
|
196
|
-
scaling them up using using ``int(num*1000)``, or fractional numbers
|
197
|
-
(stored as a numerator,denominator tuple), or ``str(Decimal(…))``.
|
198
|
-
|
199
|
-
* Don't use multiple numeric types as child nodes of a single parent.
|
200
|
-
|
201
|
-
|
202
|
-
Persistent storage
|
203
|
-
==================
|
204
|
-
|
205
|
-
MoaT-KV keeps everything in memory.
|
206
|
-
|
207
|
-
As this is not optimal if there is a power failure (or, for single-node
|
208
|
-
systems, a server crash or OS update or …), MoaT-KV has a built-in
|
209
|
-
mechanism to save its state to disk.
|
210
|
-
|
211
|
-
Automatic state save+restore
|
212
|
-
----------------------------
|
213
|
-
|
214
|
-
Do this::
|
215
|
-
|
216
|
-
$ echo MODE=hybrid >>/etc/moat/kv.env
|
217
|
-
$ /usr/lib/moat/kv/rotate
|
218
|
-
$ systemctl restart moat-kv
|
219
|
-
|
220
|
-
The MoaT-KV server will now auto-save the current state every 15 minutes,
|
221
|
-
log all changes, and load the most-recent state from disk when it's
|
222
|
-
restarted.
|
223
|
-
|
224
|
-
Use ``MODE=master`` if you use a stand-alone MoaT server.
|
225
|
-
|
226
|
-
Manual state save+restore
|
227
|
-
-------------------------
|
228
|
-
|
229
|
-
You can also save state manually::
|
230
|
-
|
231
|
-
$ moat kv log dest /var/local/lib/moat/kv/$(date +%Y%m%d).state
|
232
|
-
|
233
|
-
This command writes the current state to the given file. The server keeps the
|
234
|
-
file open and appends new records to it. The ``log dest`` has options to
|
235
|
-
write an incremental change record or to create a one-shot dump.
|
236
|
-
|
237
|
-
Incremental records are guaranteed to not have missing or duplicate records.
|
238
|
-
|
239
|
-
When you need to restart your MoaT-KV system from scratch, tell it to use the
|
240
|
-
newest saved state file::
|
241
|
-
|
242
|
-
$ moat kv server -l $(ls -t /var/local/lib/moat/kv/*.state | head -1) $(hostname)
|
243
|
-
Running.
|
244
|
-
|
245
|
-
If your state dump files are incremental, you should instead do
|
246
|
-
something like this::
|
247
|
-
|
248
|
-
$ moat kv server -l <(cat /var/local/lib/moat/kv/*.state) $(hostname)
|
249
|
-
Running.
|
250
|
-
|
251
|
-
These commands are mostly-safe to use on a network that's already
|
252
|
-
running; your node may run with old state for a few seconds until it
|
253
|
-
retrieves the updates that happened while it was down. An option to delay
|
254
|
-
startup until that process has completed is somewhere on the TODO list.
|
255
|
-
|
256
|
-
In a typical MoaT-KV network, at most two or three nodes will use persistent
|
257
|
-
storage; all others simply sync up with one of their peers whenever they
|
258
|
-
are restarted.
|
259
|
-
|
260
|
-
|
261
|
-
Authorization
|
262
|
-
=============
|
263
|
-
|
264
|
-
MoaT-KV initially doesn't use an authorization scheme. However,
|
265
|
-
advanced uses require the ability to distinguish between users.
|
266
|
-
|
267
|
-
Let's set up a "root" user::
|
268
|
-
|
269
|
-
$ moat kv auth -m password user add name=joe password?=Code
|
270
|
-
Code: ******
|
271
|
-
$ moat kv auth -m password user list
|
272
|
-
joe
|
273
|
-
$ moat kv auth -m password init -s
|
274
|
-
Authorization switched to password
|
275
|
-
$
|
276
|
-
|
277
|
-
(The input at the "Code:" prompt is not echoed.)
|
278
|
-
|
279
|
-
After this point, you can no longer use MoaT-KV without a password::
|
280
|
-
|
281
|
-
$ mkd :
|
282
|
-
ClientAuthRequiredError: You need to log in using: password
|
283
|
-
$
|
284
|
-
|
285
|
-
$ moat kv -a "password name=joe password?=Code" data :
|
286
|
-
Code: ******
|
287
|
-
"Root"
|
288
|
-
$
|
289
|
-
|
290
|
-
Internal data are stored in a separate MoaT-KV subtree that starts with a ``None`` value.
|
291
|
-
You can display it::
|
292
|
-
|
293
|
-
$ moat kv -a "password name=joe password=test123" data internal dump :
|
294
|
-
null:
|
295
|
-
auth:
|
296
|
-
_:
|
297
|
-
current: password
|
298
|
-
password:
|
299
|
-
user:
|
300
|
-
joe:
|
301
|
-
_:
|
302
|
-
_aux: null
|
303
|
-
password: !!binary |
|
304
|
-
7NcYcNGWMxapfjrDQIyYNa2M8PPBvHA1J8MCZVNPda4=
|
305
|
-
|
306
|
-
As you can see, passwords are encrypted -- hashed, actually. The exact
|
307
|
-
scheme depends on the auth method.
|
308
|
-
|
309
|
-
NB: nothing prevents you from using the string ``"null"`` as an ordinary
|
310
|
-
key name::
|
311
|
-
|
312
|
-
$ moat kv -a "password name=joe password=test123" data null.foo set -v : bar
|
313
|
-
$ moat kv -a "password name=joe password=test123" data : get -rd_
|
314
|
-
…
|
315
|
-
'null':
|
316
|
-
foo:
|
317
|
-
_: bar
|
318
|
-
|
319
|
-
For experimentation, there's also a ``_test`` authorization method which
|
320
|
-
only exposes a user name::
|
321
|
-
|
322
|
-
$ moat kv auth -m _test user add name=joe
|
323
|
-
$ moat kv auth -m _test user add name=root
|
324
|
-
$ moat kv auth -m _test init
|
325
|
-
$ moat kv data :
|
326
|
-
ClientAuthRequiredError: You need to log in using: _test
|
327
|
-
$ mkv() { moat kv -a "_test name=joe" "$@"; }
|
328
|
-
$ mkv data :
|
329
|
-
123
|
330
|
-
$
|
331
|
-
|
332
|
-
We'll use this user, and the shell alias, in the following sections.
|
333
|
-
|
334
|
-
ACLs and distributed servers
|
335
|
-
----------------------------
|
336
|
-
|
337
|
-
MoaT-KV servers actually use the client protocol when they sync up. Thus, when you
|
338
|
-
set up authorization, you must teach your servers to authenticate to their
|
339
|
-
peer::
|
340
|
-
|
341
|
-
$ moat kv -C connect.auth="_test name=joe" server $(hostname)
|
342
|
-
|
343
|
-
You typically store that in a configuration file::
|
344
|
-
|
345
|
-
kv:
|
346
|
-
conn:
|
347
|
-
auth: "_test name=joe"
|
348
|
-
host: 127.0.0.1
|
349
|
-
|
350
|
-
``moat`` auto-reads the configuration from a few paths, or you can use
|
351
|
-
the ``moat -c test.cfg`` flag.
|
352
|
-
|
353
|
-
Access restrictions
|
354
|
-
===================
|
355
|
-
|
356
|
-
A user can be restricted from accessing or modifying MoaT-KV data.
|
357
|
-
|
358
|
-
Let's say that we'd like to create a "write-only" data storage::
|
359
|
-
|
360
|
-
$ moat kv -a "_test name=root" acl set writeonly -a xc 'wom.#'
|
361
|
-
$ moat kv -a "_test name=root" auth user set param joe acl writeonly
|
362
|
-
$ mkv data wom.foo.bar set -e : 42
|
363
|
-
$ mkv data wom.foo.bar set -e : 43
|
364
|
-
ServerError: (<AclEntry:[None, 'acl', 'writeonly', 'wom', '#']@<NodeEvent:<Node: test1 @10> @4 1> ='cx'>, 'w')
|
365
|
-
$ mkv data wom.foo
|
366
|
-
ServerError: (<AclEntry:[None, 'acl', 'writeonly', 'wom', '#']@<NodeEvent:<Node: test1 @10> @4 1> ='cx'>, 'r')
|
367
|
-
$
|
368
|
-
|
369
|
-
As you can see, this allows the user to write to arbitrary values to the
|
370
|
-
"wom" tree, but Joe cannot change anything – nor can he read the values
|
371
|
-
which he wrote.
|
372
|
-
|
373
|
-
Note that we also created a "root" user who doesn't have ACL restrictions.
|
374
|
-
If we had not, we'd now be locked out of our MoaT-KV storage because "no
|
375
|
-
matching ACL" means "no access".
|
376
|
-
|
377
|
-
A user who has an ACL set can no longer modify the system, because the
|
378
|
-
``None`` element that separates system data from the rest cannot match a
|
379
|
-
wildcard. ACLs for system entries are on the TODO list; so are user groups
|
380
|
-
or roles or whatever. Code welcome.
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
Code execution
|
385
|
-
==============
|
386
|
-
|
387
|
-
MoaT-KV doesn't just store passive data: you can also use it to distribute
|
388
|
-
actual computing. We'll demonstrate that here.
|
389
|
-
|
390
|
-
First we feed some interesting code into MoaT-KV::
|
391
|
-
|
392
|
-
$ mkv code set the.answer <<END
|
393
|
-
> print("Forty-Two!")
|
394
|
-
> return 42
|
395
|
-
> END
|
396
|
-
|
397
|
-
Then we set up a one-shot run-anywhere instance::
|
398
|
-
|
399
|
-
$ mkv run set -c the.answer -t 0 a.question
|
400
|
-
|
401
|
-
This doesn't actually execute any code because the executor is not part of
|
402
|
-
the MoaT-KV server. (The server may gain an option to do that too, but
|
403
|
-
not yet.) So we run it::
|
404
|
-
|
405
|
-
$ mkv run all
|
406
|
-
Forty-Two!
|
407
|
-
|
408
|
-
(Initially this takes some time, because the ``run`` command needs to
|
409
|
-
co-ordinate with other runners. There aren't any, others, of course, but
|
410
|
-
MoaT-KV can't know that.)
|
411
|
-
|
412
|
-
The code will not run again unless we either re-set ``--time``, or set a
|
413
|
-
repeat timer with ``--repeat``.
|
414
|
-
|
415
|
-
Start times are mostly-accurate. There are two reasons why they might not
|
416
|
-
be:
|
417
|
-
|
418
|
-
* the co-ordination system has a periodic window where it waits for the
|
419
|
-
next coordinator. This causes a delay of up to two seconds.
|
420
|
-
|
421
|
-
* TODO: The current leader might decide that it's too busy and wants to
|
422
|
-
delegate starting a particular job to some other node in the cluster.
|
423
|
-
This incurs some delay, more if the recipient is no longer available.
|
424
|
-
|
425
|
-
This method will run the code in question on any node. You can also run
|
426
|
-
code on one specific node; simply do::
|
427
|
-
|
428
|
-
$ mkv run -n $(hostname) set -c "same answer" -t 0 a.question
|
429
|
-
$ mkv run -n $(hostname) all
|
430
|
-
|
431
|
-
The one-node-only runner and the any-node runner are distinct. There's also
|
432
|
-
a way to designate a subgroup of hosts (like "all with a 1wire interface")
|
433
|
-
and to run a job on any / all of them. See ``moat kv run --help`` for details.
|
434
|
-
|
435
|
-
|
436
|
-
Errors
|
437
|
-
======
|
438
|
-
|
439
|
-
Nobody is perfect, and neither is code. Sometimes things break.
|
440
|
-
MoaT-KV remembers errors. To demonstrate, let's first provoke one::
|
441
|
-
|
442
|
-
$ mkv code set the.error <<END
|
443
|
-
> raise RuntimeError("Owch")
|
444
|
-
> END
|
445
|
-
$ mkv run set -c the.error -t 0 what.me.worry
|
446
|
-
$ mkv run all # if it's not still running
|
447
|
-
20:24:13.935 WARNING:moat.kv.errors:Error ('.moat', 'kv', 'error', 'test1', 16373) test1: Exception: Owch
|
448
|
-
|
449
|
-
The list of errors is now no longer empty::
|
450
|
-
|
451
|
-
$ mkv error list -d_
|
452
|
-
[ some YAML ]
|
453
|
-
|
454
|
-
You can limit the error list to specific subtrees. This command has the
|
455
|
-
same effect::
|
456
|
-
|
457
|
-
$ mkv error list -d_ :.moat.kv.run.any
|
458
|
-
|
459
|
-
except that the path is shortened for improved useability.
|
460
|
-
|
461
|
-
Error details are available; add the ``-a`` option. You can also filter
|
462
|
-
errors on a specific node, which only includes that node's details.
|
463
|
-
|
464
|
-
|
465
|
-
The Python API
|
466
|
-
==============
|
467
|
-
|
468
|
-
Command lines are all well and good, but MoaT-KV gets really interesting
|
469
|
-
when you use it from Python.
|
470
|
-
|
471
|
-
Let's start by simply setting some value::
|
472
|
-
|
473
|
-
import anyio
|
474
|
-
from moat.kv.client import open_client
|
475
|
-
from moat.util import P
|
476
|
-
|
477
|
-
async def dkv_example():
|
478
|
-
async with open_client() as client:
|
479
|
-
client.set(P("one.two.three"), value=("Test",42,False), chain=None)
|
480
|
-
|
481
|
-
anyio.run(dkv_example)
|
482
|
-
|
483
|
-
That was easy. Now we'd like to update that entry::
|
484
|
-
|
485
|
-
from moat.util import P
|
486
|
-
async def dkv_example():
|
487
|
-
async with open_client() as client:
|
488
|
-
res = client.get(P("one.two.three"), nchain=2)
|
489
|
-
ret = client.set(P("one.two.three"), value=("Test",v[1]+1,False), chain=res.chain)
|
490
|
-
assert res.chain != ret.chain
|
491
|
-
|
492
|
-
The ``chain`` parameter is important: it tells MoaT-KV which change caused
|
493
|
-
the old value. So if somebody else changes your ``one.two.three`` entry
|
494
|
-
while your program was running, you get a collision and the ``set`` fails.
|
495
|
-
|
496
|
-
``set`` returns a new chain so you can update your value multiple times.
|
497
|
-
|
498
|
-
Deleting an entry clears the chain because the source of a non-existing value
|
499
|
-
doesn't matter.
|
500
|
-
|
501
|
-
.. warning::
|
502
|
-
MoaT-KV is an asynchronous distributed system. Thus, asuming that you
|
503
|
-
have more than one MoaT-KV server, this does not prevent your ``set``
|
504
|
-
command from being ignored; it just reduces the window when this could
|
505
|
-
happen from the time since the last ``get`` to a couple of milliseconds.
|
506
|
-
|
507
|
-
|
508
|
-
Watching for Changes
|
509
|
-
--------------------
|
510
|
-
|
511
|
-
The result of the previous ``get`` was static. If somebody else
|
512
|
-
subsequently changes it, you wouldn't know. Let's fix that::
|
513
|
-
|
514
|
-
async def dkv_example():
|
515
|
-
async with open_client() as client:
|
516
|
-
async with client.watch(P("one.two"), fetch=True) as watcher:
|
517
|
-
async for res in watcher:
|
518
|
-
if 'path' not in res:
|
519
|
-
continue
|
520
|
-
if 'value' in res:
|
521
|
-
print(f"{path}= {res.value}")
|
522
|
-
else:
|
523
|
-
print(f"{path}: deleted")
|
524
|
-
|
525
|
-
``fetch=True`` will send the current state in addition to any changes.
|
526
|
-
The ``'path' not in res`` test filters the notification that tells you that
|
527
|
-
the subtree you requested is complete. The result's path doesn't contain
|
528
|
-
the prefix you used in ``watch`` because you already know it.
|
529
|
-
|
530
|
-
if you need two ``watch`` at the same time, create separate tasks. Feed the
|
531
|
-
resuts through a common queue if you want to process them in a comon
|
532
|
-
function.
|
533
|
-
|
534
|
-
Active objects
|
535
|
-
--------------
|
536
|
-
|
537
|
-
While watching for changes is nice, organizing the resulting objects tends
|
538
|
-
to be tedious. MoaT-KV comes with a couple of classes that does this for you::
|
539
|
-
|
540
|
-
from moat.kv.obj import ClientRoot, ClientEntry
|
541
|
-
from moat.util import NotGiven
|
542
|
-
|
543
|
-
class OneEntry(ClientEntry):
|
544
|
-
async def set(self, value):
|
545
|
-
await super().set_value()
|
546
|
-
path = ' '.join(str(x) for x in self.subpath)
|
547
|
-
if value is NotGiven:
|
548
|
-
print(f"{path}= {value}")
|
549
|
-
else:
|
550
|
-
print(f"{path}: deleted")
|
551
|
-
|
552
|
-
class OneRoot(ClientRoot):
|
553
|
-
@classmethod
|
554
|
-
def child_type(cls, name):
|
555
|
-
return OneEntry
|
556
|
-
|
557
|
-
async def dkv_example():
|
558
|
-
async with open_client() as client:
|
559
|
-
async with client.mirror("one", root_type=OneRoot) as root:
|
560
|
-
# At this point you have the sub-tree in memory
|
561
|
-
assert root['two']['three'].value[1] >= 42
|
562
|
-
|
563
|
-
await anyio.sleep_forever()
|
564
|
-
pass
|
565
|
-
# at this point the sub-tree is still there, but won't be updated
|
566
|
-
|
567
|
-
except that in a real program you'd do some real work instead of sleeping.
|
568
|
-
|
569
|
-
Verification
|
570
|
-
============
|
571
|
-
|
572
|
-
Complex data should be clean. Storing ``"Hello there!"`` in a value that
|
573
|
-
the rest of your code expects to be an integer is likely to have unwanted
|
574
|
-
effects.
|
575
|
-
|
576
|
-
For this example, we'd like to enforce that all ``quota`` values in our
|
577
|
-
site statistics are integer percentages.
|
578
|
-
|
579
|
-
First, we define the type::
|
580
|
-
|
581
|
-
$ ./kv client type set -g 0 -g -2 -g 123 -b 1.2 -b '"Hello"' int <<END
|
582
|
-
> if int(value) != value: raise ValueError("not an integer")
|
583
|
-
> END
|
584
|
-
$
|
585
|
-
|
586
|
-
As you can see, data types must be accompanied by example values that include
|
587
|
-
both "good" and "bad" examples.
|
588
|
-
|
589
|
-
You can also declare subtypes::
|
590
|
-
|
591
|
-
$ mkv type set -g 0 -g 99 -g 100 -b -1 -b 101 int.percent <<END
|
592
|
-
> if not (0 <= value <= 100): raise ValueError("not a percentage")
|
593
|
-
> END
|
594
|
-
$
|
595
|
-
|
596
|
-
The example values, both good and bad, must pass the supertype's checks.
|
597
|
-
|
598
|
-
Now we associate the test with our data::
|
599
|
-
|
600
|
-
$ mkv type match -t int.percent 'stats.#.quota'
|
601
|
-
|
602
|
-
Then we store some value::
|
603
|
-
|
604
|
-
$ mkv data stats.foo.bar.quota set -v : 123
|
605
|
-
ServerError: ValueError("not an integer")
|
606
|
-
|
607
|
-
Oops: non-string values need to be evaluated. Better::
|
608
|
-
|
609
|
-
$ mkv data stats.foo.bar.quota set -e : 123
|
610
|
-
ServerError: ValueError('not a percentage')
|
611
|
-
$ mkv data stats.foo.bar.quota set -e : 12
|
612
|
-
$
|
613
|
-
|
614
|
-
MoaT-KV does not test that existing values match your restrictions.
|
615
|
-
|
616
|
-
|
617
|
-
Data mangling
|
618
|
-
=============
|
619
|
-
|
620
|
-
Structured data are great, but some clients want boring single-value items.
|
621
|
-
For instance, some home automation systems want to use ``"ON"`` and
|
622
|
-
``"OFF"`` messages, while your active code is much happier with a ``bool``
|
623
|
-
value – or even a mapping that also carries the time of last change, so that
|
624
|
-
a ``turn off after 15 minutes`` rule will actually work.
|
625
|
-
|
626
|
-
Let's write a simple number codec::
|
627
|
-
|
628
|
-
$ mkv codec set -i '"12.5"' 12.5 -o 13.25 '"13.25"' float.str
|
629
|
-
Enter the Python script to encode 'value'.
|
630
|
-
return str(value)
|
631
|
-
Enter the Python script to decode 'value'.
|
632
|
-
return float(value)
|
633
|
-
^D
|
634
|
-
|
635
|
-
As you can see, you need to give the codec some examples. Here they're
|
636
|
-
symmetric but that's not a requirement; for instance, a ``bool`` codec for our
|
637
|
-
home automation system could accept a wide range of ``true``-ish or
|
638
|
-
``false``-ish strings but it would always output ``ON`` and ``OFF``.
|
639
|
-
|
640
|
-
Associating this codec with a path is slightly more involved::
|
641
|
-
|
642
|
-
$ mkv codec convert -c float.str floatval 'monitor.#.value'
|
643
|
-
|
644
|
-
This associates
|
645
|
-
|
646
|
-
* the float-to-string codec we just created
|
647
|
-
|
648
|
-
* all paths that start with ``monitor`` and end with ``value``
|
649
|
-
|
650
|
-
with the codec list named ``floatval``. As not every user needs stringified
|
651
|
-
numbers, we also need to tell MoaT-KV which users to apply this codec to::
|
652
|
-
|
653
|
-
$ mkv auth user modify --aux codec=floatval name=joe
|
654
|
-
|
655
|
-
Thus, Joe will read and write ``value`` entries as strings::
|
656
|
-
|
657
|
-
$ mkv data monitor.a.b.c.value set -v : 99.5
|
658
|
-
$ mkv data monitor.a.b.c.thing set -v : 12.3
|
659
|
-
$ mkv data monitor get -rd_
|
660
|
-
a:
|
661
|
-
b:
|
662
|
-
c:
|
663
|
-
value:
|
664
|
-
_:
|
665
|
-
99.5
|
666
|
-
thing:
|
667
|
-
_:
|
668
|
-
'12.3'
|
669
|
-
|
670
|
-
This is especially helpful if Joe is in fact an MQTT gateway which only
|
671
|
-
receives and transmits strings. A real-world application would use
|
672
|
-
binary strings, not Unicode strings.
|
673
|
-
|
674
|
-
|
675
|
-
Limitations
|
676
|
-
-----------
|
677
|
-
|
678
|
-
MoaT-KV currently can't translate paths, or merge many values to one entry's attributes.
|
679
|
-
|
680
|
-
You can use either active objects (add some code to their ``set_value``
|
681
|
-
methods) or code objects (listen to A and write to B) to effect such
|
682
|
-
translations. There are some caveats:
|
683
|
-
|
684
|
-
* All such data are stored twice.
|
685
|
-
|
686
|
-
* Replacing a value with the exact same value still counts as a change.
|
687
|
-
Don't set up an endless loop.
|
688
|
-
|
689
|
-
* You need to verify that the two trees match when you start up, and decide
|
690
|
-
which is more correct. (The ``tock`` stamp will help you here.) Don't
|
691
|
-
overwrite changes that arrive while you do that.
|
692
|
-
|
693
|
-
|
694
|
-
Dynamic configuration
|
695
|
-
=====================
|
696
|
-
|
697
|
-
For some use cases, you might want to configure MoaT-KV dynamically instead
|
698
|
-
of by a static configuration file.
|
699
|
-
|
700
|
-
This is not always feasible; in particular, the "logging" and "server"
|
701
|
-
sections are imported once. Also, options used for connecting to another
|
702
|
-
MoaT-KV server cannot be set dynamically because you need them before the
|
703
|
-
data are available.
|
704
|
-
|
705
|
-
Other options may be overridden by storing a new values at ``.moat kv config
|
706
|
-
<name>``. It is not possible to be more specific. (TODO)
|
707
|
-
|
708
|
-
If a client's ACLs do not allow reading a config entry, it will be silently
|
709
|
-
ignored.
|
710
|
-
|
711
|
-
A config entry's ``_watch`` property will trigger when the entry is updated.
|