Compare commits
2000 Commits
0.0.7
...
1787e44503
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1787e44503 | ||
|
|
bc4f7b7309 | ||
|
|
f7d8b42c7b | ||
|
|
1fb3ef1e71 | ||
|
|
3e153f7f44 | ||
|
|
8b30f7bd3d | ||
|
|
d304dafeea | ||
|
|
9271906435 | ||
|
|
7146139ae8 | ||
|
|
ef74ede862 | ||
|
|
56e55dfad7 | ||
|
|
303e80dee7 | ||
|
|
6c6c9df75e | ||
|
|
5f4ae05340 | ||
|
|
c48b884d16 | ||
|
|
fe77bce544 | ||
|
|
26640df164 | ||
|
|
debd4ee653 | ||
|
|
63269fe343 | ||
|
|
f588703474 | ||
|
|
52fbc31da7 | ||
|
|
154f900d2a | ||
|
|
bbd4f1dea1 | ||
|
|
22120649e5 | ||
|
|
a840dd43dd | ||
|
|
83743df3e4 | ||
|
|
15b2ff68cc | ||
|
|
17544e881c | ||
|
|
6090517830 | ||
|
|
6a7b3d5fa9 | ||
|
|
25804c71df | ||
|
|
a54553c858 | ||
|
|
ff1581be87 | ||
|
|
ec48a6944c | ||
|
|
df9849319d | ||
|
|
045aa8820c | ||
|
|
a66cbc3098 | ||
|
|
9833cb8df3 | ||
|
|
a73dc19ce9 | ||
|
|
bcf57f8581 | ||
|
|
611cdb3193 | ||
|
|
1b39a780e1 | ||
|
|
d59ae2e007 | ||
|
|
b9363a3463 | ||
|
|
a5f89d8d7b | ||
|
|
790b7199ca | ||
|
|
1e2db5b8c5 | ||
|
|
6041c5dc22 | ||
|
|
a668cd847e | ||
|
|
474df66ca0 | ||
|
|
2022a31820 | ||
|
|
71756df815 | ||
|
|
f808c1ea0c | ||
|
|
6c206b8010 | ||
|
|
fb8958f192 | ||
|
|
a070722937 | ||
|
|
b02363ad0d | ||
|
|
bed7e6be79 | ||
|
|
0031acbcbc | ||
|
|
4e523231d6 | ||
|
|
05d72b339d | ||
|
|
d2cda4ca78 | ||
|
|
2f860be5fe | ||
|
|
5692837175 | ||
|
|
04ff2a8c5c | ||
|
|
166b463fa9 | ||
|
|
2e858ff447 | ||
|
|
6fac46da7c | ||
|
|
2b820da311 | ||
|
|
86c6ee8dee | ||
|
|
0a76910902 | ||
|
|
138ff8a895 | ||
|
|
8edb8e2a7b | ||
|
|
bdf5209f68 | ||
|
|
299b49bb17 | ||
|
|
71e4282d3f | ||
|
|
984aa0f5fc | ||
|
|
737d1c9004 | ||
|
|
d113fcb6d7 | ||
|
|
73e641120f | ||
|
|
f7e446ef6f | ||
|
|
21917864db | ||
|
|
c49d469dcd | ||
|
|
0a79daf277 | ||
|
|
a4ed647d02 | ||
|
|
79c199975d | ||
|
|
50b4a2a4f7 | ||
|
|
f778008929 | ||
|
|
54380a2a1f | ||
|
|
a5fc1256e2 | ||
|
|
0b2236962c | ||
|
|
ee7ad7cbbe | ||
|
|
7ba4c4960b | ||
|
|
777ea6115b | ||
|
|
582cea31b0 | ||
|
|
c107240098 | ||
|
|
6265a330bf | ||
|
|
cfcb35456f | ||
|
|
1ef7c370e7 | ||
|
|
f22ec07ed3 | ||
|
|
f594799a7f | ||
|
|
1ccec72a7c | ||
|
|
55eeb50fb4 | ||
|
|
2b7e9c3200 | ||
|
|
25263fe9ea | ||
|
|
1df28c1d00 | ||
|
|
32e91dc7de | ||
|
|
c2c6cb5b6a | ||
|
|
58461323b9 | ||
|
|
cdc6743d83 | ||
|
|
86dfa5844a | ||
|
|
5d44cd28db | ||
|
|
4f977dbe57 | ||
|
|
573bd283cd | ||
|
|
6ac72974eb | ||
|
|
4189a1299a | ||
|
|
dcd4f0709f | ||
|
|
75bafa4fd3 | ||
|
|
e5ec13f592 | ||
|
|
1a0fcfb829 | ||
|
|
ba86665cbb | ||
|
|
301ce03061 | ||
|
|
650e8392c5 | ||
|
|
d7534b2b3b | ||
|
|
3b88ad3794 | ||
|
|
499b8f2732 | ||
|
|
ac3a131a9f | ||
|
|
a72492f042 | ||
|
|
c51a55e98a | ||
|
|
892fd1e691 | ||
|
|
23aa18d363 | ||
|
|
d14930ef28 | ||
|
|
81063ae6df | ||
|
|
f42daf4509 | ||
|
|
1caf6fb3bf | ||
|
|
16ade43caf | ||
|
|
99d8846934 | ||
|
|
2d78dc9836 | ||
|
|
b85751e07e | ||
|
|
0fd6970c0a | ||
|
|
936cf7dd9d | ||
|
|
fd5bc63293 | ||
|
|
be4cb6658e | ||
|
|
efff84bbd4 | ||
|
|
74f36c5d73 | ||
|
|
b868a60f69 | ||
|
|
f73127ec23 | ||
|
|
654e958d3f | ||
|
|
1f54253f95 | ||
|
|
2948644536 | ||
|
|
d2403d2f0c | ||
|
|
876834ff29 | ||
|
|
861ba50f9c | ||
|
|
43492a8cfa | ||
|
|
287504cfa8 | ||
|
|
0847b27f6a | ||
|
|
aa4320dd5f | ||
|
|
7c5adb1fec | ||
|
|
20e1c461b8 | ||
|
|
e9d485b7f6 | ||
|
|
e86d66b906 | ||
|
|
9a63fadfbd | ||
|
|
7afa372765 | ||
|
|
fddebb2474 | ||
|
|
ad0dd44130 | ||
|
|
2ee403bab9 | ||
|
|
0e34f13cce | ||
|
|
f2a6a6769f | ||
|
|
4903995052 | ||
|
|
774d408e13 | ||
|
|
ae1d9b94d4 | ||
|
|
267bcc144b | ||
|
|
fd40c3b64f | ||
|
|
e2b6da01d8 | ||
|
|
dad15f6adc | ||
|
|
6ec707aea7 | ||
|
|
807c4b3430 | ||
|
|
6b10477ebc | ||
|
|
412e480b44 | ||
|
|
cc2a235fbb | ||
|
|
7c77efec1d | ||
|
|
4b1548488d | ||
|
|
47aecd25c3 | ||
|
|
fb6eae184a | ||
|
|
16d3e3063c | ||
|
|
37bb67dffd | ||
|
|
9c9f2f558a | ||
|
|
1a81e57410 | ||
|
|
7096293885 | ||
|
|
1536a94026 | ||
|
|
1bb1e056c4 | ||
|
|
e71b11f843 | ||
|
|
b4769eefd9 | ||
|
|
d4a24d4c9d | ||
|
|
c5d7fdb0a3 | ||
|
|
ae68dd79cb | ||
|
|
de970ee557 | ||
|
|
60a3d7c65e | ||
|
|
9c1c587f7b | ||
|
|
af04d364d0 | ||
|
|
748f05732a | ||
|
|
148bd50e9f | ||
|
|
6c1c08ceda | ||
|
|
045b29291e | ||
|
|
955112f64f | ||
|
|
7f341cee84 | ||
|
|
f71e623931 | ||
|
|
8ff187b4e9 | ||
|
|
30aca74089 | ||
|
|
3dfca97f86 | ||
|
|
0d4c6e272d | ||
|
|
fce250b8af | ||
|
|
f6a8404f9f | ||
|
|
c50a578426 | ||
|
|
7e8ced534f | ||
|
|
f2d9219218 | ||
|
|
f269096eb9 | ||
|
|
5665259784 | ||
|
|
02fca6409a | ||
|
|
a7a5237b07 | ||
|
|
7ad54fe3e8 | ||
|
|
1a35ab61ca | ||
|
|
59c33a354c | ||
|
|
c853e24ded | ||
|
|
692db084e4 | ||
|
|
1edff3b3f5 | ||
|
|
b173d9f8ef | ||
|
|
a697add8d0 | ||
|
|
c83e2cb877 | ||
|
|
642c6b952f | ||
|
|
f313380480 | ||
|
|
f8a4751290 | ||
|
|
3ca1aa9cb1 | ||
|
|
37308b950b | ||
|
|
05306e3729 | ||
|
|
a6057319a9 | ||
|
|
87d8533bd0 | ||
|
|
dce83efa96 | ||
|
|
1cb9648b08 | ||
|
|
17b859d0d7 | ||
|
|
8d34910b9b | ||
|
|
5667fec410 | ||
|
|
46035fee83 | ||
|
|
219d25b330 | ||
|
|
181aab9c81 | ||
|
|
aabcaa7c8c | ||
|
|
09f3b8c05f | ||
|
|
f5e2fde20d | ||
|
|
50bd6f5811 | ||
|
|
37e5a37045 | ||
|
|
8544a66257 | ||
|
|
a50765393d | ||
|
|
6bae5fc561 | ||
|
|
f87c550be1 | ||
|
|
aea894a706 | ||
|
|
a549a30f71 | ||
|
|
2899bc234a | ||
|
|
cf7e73bbf6 | ||
|
|
c456a5ab97 | ||
|
|
b5ae96e0d4 | ||
|
|
f792facde9 | ||
|
|
a64e3ee179 | ||
|
|
c5257dd64b | ||
|
|
f74bc969ca | ||
|
|
63d7b8e51e | ||
|
|
d56896cb0d | ||
|
|
d579787bcd | ||
|
|
37fffce9f5 | ||
|
|
d7ecc72b41 | ||
|
|
f754bbbf90 | ||
|
|
bb171ced86 | ||
|
|
c25a2a257b | ||
|
|
1f90de31e7 | ||
|
|
b5384d1278 | ||
|
|
d80ec4aaa7 | ||
|
|
5d63376087 | ||
|
|
4fd6ced287 | ||
|
|
82489c3fe0 | ||
|
|
a064a87ecd | ||
|
|
f51a1200d1 | ||
|
|
ecd5a0f304 | ||
|
|
096ef4cc66 | ||
|
|
7da98ef349 | ||
|
|
8cd7fa27e2 | ||
|
|
134e2f1cd9 | ||
|
|
042ae02428 | ||
|
|
1e33c1fdae | ||
|
|
bdc46648ff | ||
|
|
ab9c1d3d96 | ||
|
|
0fb546ad61 | ||
|
|
7439d532c7 | ||
|
|
de9c0adcc0 | ||
|
|
e40819d617 | ||
|
|
7331d3a7ee | ||
|
|
95f353c6a4 | ||
|
|
5044ef4e8a | ||
|
|
3c61d088ab | ||
|
|
315a493565 | ||
|
|
6268b61a7d | ||
|
|
3f202c6a7a | ||
|
|
d46c43df5a | ||
|
|
1538befc93 | ||
|
|
1af334f2ce | ||
|
|
d30ea571f1 | ||
|
|
d30ff6cfae | ||
|
|
1d3f2dbe3c | ||
|
|
ca6e7ad432 | ||
|
|
f92afe9ae4 | ||
|
|
483cc22c32 | ||
|
|
2f3bd72491 | ||
|
|
6499fcb1e0 | ||
|
|
12a0600d38 | ||
|
|
cace2bacb8 | ||
|
|
05d440114a | ||
|
|
b392285e1d | ||
|
|
a713c08585 | ||
|
|
8e8e831e73 | ||
|
|
86b95b2c0b | ||
|
|
4a578ca40c | ||
|
|
a60148f370 | ||
|
|
00366de67b | ||
|
|
a08ba0b0e9 | ||
|
|
81b102ed7f | ||
|
|
c8f911ec5d | ||
|
|
7694da4241 | ||
|
|
a0d500a602 | ||
|
|
553172992f | ||
|
|
e6d614f4dd | ||
|
|
3107dfbd08 | ||
|
|
802823dcb0 | ||
|
|
5858c8b501 | ||
|
|
2561dba8f5 | ||
|
|
f5806e0617 | ||
|
|
e9dbb7b86c | ||
|
|
28f5b8331a | ||
|
|
5ff4f0456a | ||
|
|
82c614f2d9 | ||
|
|
50265d2303 | ||
|
|
ecee84aa28 | ||
|
|
2e146e8c8e | ||
|
|
097efdd66a | ||
|
|
5764c977f1 | ||
|
|
4d30772b3b | ||
|
|
8472b1ebf2 | ||
|
|
e1070d3e13 | ||
|
|
98d7f294eb | ||
|
|
517fc1e05b | ||
|
|
c2f75d64a6 | ||
|
|
380004b1dc | ||
|
|
28a443d11d | ||
|
|
a600e11100 | ||
|
|
7b45f94bb0 | ||
|
|
acdd6476f2 | ||
|
|
018d3efc90 | ||
|
|
b40d10a366 | ||
|
|
a88034ab06 | ||
|
|
907d2ad1a1 | ||
|
|
3bd6986fde | ||
|
|
43bd847bad | ||
|
|
0c0583adc8 | ||
|
|
c642b5eeae | ||
|
|
69e84fbbed | ||
|
|
f8b06f32ec | ||
|
|
59a20f53eb | ||
|
|
83fd8b7e54 | ||
|
|
098ab20ec9 | ||
|
|
a2ce9e890d | ||
|
|
be7a5399e3 | ||
|
|
3fb492f6aa | ||
|
|
e4f062b006 | ||
|
|
422719c345 | ||
|
|
71a1efde99 | ||
|
|
ed84c5460c | ||
|
|
0222a682fc | ||
|
|
1cd4af5838 | ||
|
|
d1aaf6e82b | ||
|
|
52a71f9515 | ||
|
|
3c665174cc | ||
|
|
93eb8b2b76 | ||
|
|
1692235498 | ||
|
|
a6bcd4b92b | ||
|
|
d065cddf5e | ||
|
|
20d4809e8e | ||
|
|
b074386c26 | ||
|
|
b140b2dfeb | ||
|
|
8e3d959500 | ||
|
|
8c886bbe7c | ||
|
|
7d204dfb74 | ||
|
|
583f90dc7b | ||
|
|
85e1d6c0e8 | ||
|
|
2c967e3897 | ||
|
|
202a8e1fba | ||
|
|
e6085d77ff | ||
|
|
10f82c6566 | ||
|
|
3d11b2caaf | ||
|
|
f8037a1f99 | ||
|
|
067eef9007 | ||
|
|
e45d9be065 | ||
|
|
d24149518c | ||
|
|
d403f18b2a | ||
|
|
1f12150d8f | ||
|
|
d3a7cefcc6 | ||
|
|
a8c8f09aa3 | ||
|
|
b03fdeccae | ||
|
|
6c12e8a29b | ||
|
|
310452542b | ||
|
|
b514022713 | ||
|
|
c937280664 | ||
|
|
898b58e3e7 | ||
|
|
74119a0a53 | ||
|
|
d6914d3437 | ||
|
|
fdfa03685c | ||
|
|
149a85fcde | ||
|
|
65f26769ae | ||
|
|
6397c8f930 | ||
|
|
761030b5b8 | ||
|
|
9a752da13d | ||
|
|
13fc711657 | ||
|
|
6419f931ee | ||
|
|
562138cb74 | ||
|
|
8aac770bcb | ||
|
|
80e8c9cadc | ||
|
|
87b3dda867 | ||
|
|
b9e093cd6b | ||
|
|
06a023ca66 | ||
|
|
ccb4c6244d | ||
|
|
4489e5ce6e | ||
|
|
8df82f0301 | ||
|
|
57b4a7efce | ||
|
|
fd508fbc0d | ||
|
|
a4f368fc9f | ||
|
|
e7b57a32fd | ||
|
|
06cc63fcb6 | ||
|
|
e34212a10b | ||
|
|
5f6e07b5e8 | ||
|
|
1465c5cdc9 | ||
|
|
29eebd0d07 | ||
|
|
5bbc06d8bc | ||
|
|
9a5f6a5bd3 | ||
|
|
2e774215e4 | ||
|
|
1327752725 | ||
|
|
118f266211 | ||
|
|
87a2dfc8f9 | ||
|
|
b88ac4603f | ||
|
|
28e81bcca3 | ||
|
|
3d0660559e | ||
|
|
48dc9ad099 | ||
|
|
fd3a2a1f0f | ||
|
|
c6e9175e3f | ||
|
|
1a39472734 | ||
|
|
bfa88e9b1c | ||
|
|
a0972c0752 | ||
|
|
8dc0d44513 | ||
|
|
8594b6e2a9 | ||
|
|
82cac572ca | ||
|
|
da4f69cd87 | ||
|
|
e6cb776eb6 | ||
|
|
7557114b4e | ||
|
|
001e1a5da0 | ||
|
|
6f3c3c318b | ||
|
|
654e376be7 | ||
|
|
211121cdca | ||
|
|
f2d4cac92d | ||
|
|
c5dc9c7650 | ||
|
|
7596f5b572 | ||
|
|
8e9c3b6c1e | ||
|
|
a93c98402a | ||
|
|
b04ee4ba22 | ||
|
|
65b104ea55 | ||
|
|
562eb643fc | ||
|
|
80178422db | ||
|
|
e94f39bf2c | ||
|
|
6c1a33066a | ||
|
|
beca0c3ae6 | ||
|
|
7517c83953 | ||
|
|
0354082f89 | ||
|
|
4abcd9cf01 | ||
|
|
c974820c56 | ||
|
|
88670ae7a1 | ||
|
|
d0ed004b24 | ||
|
|
6de7d8b254 | ||
|
|
bfb5d983c1 | ||
|
|
0a183dfff9 | ||
|
|
8b54306eb9 | ||
|
|
fd86b35ce3 | ||
|
|
d9f8dd53c1 | ||
|
|
ccb0e55d5a | ||
|
|
74f747e80b | ||
|
|
aa03b5ce2f | ||
|
|
e747e12002 | ||
|
|
d1753c592a | ||
|
|
7a35bef7ac | ||
|
|
e10e92596f | ||
|
|
28253c4bd2 | ||
|
|
f2976deb02 | ||
|
|
14577a0c46 | ||
|
|
4e18c9c67a | ||
|
|
d326917432 | ||
|
|
ad4eb86262 | ||
|
|
5c73e7c582 | ||
|
|
dc33d9aab7 | ||
|
|
cdc2439f89 | ||
|
|
318ee0d002 | ||
|
|
653299a88f | ||
|
|
6066cbf075 | ||
|
|
2b3a41fefa | ||
|
|
5ca9f7fa38 | ||
|
|
201cf091d5 | ||
|
|
09e53bfd3f | ||
|
|
3c661ab674 | ||
|
|
415e22abe2 | ||
|
|
3b754d5324 | ||
|
|
7a568627e9 | ||
|
|
328360eea8 | ||
|
|
7ae3ba4483 | ||
|
|
351a61c0cd | ||
|
|
c12452b3ce | ||
|
|
0e92d190cc | ||
|
|
453cd4409e | ||
|
|
51cf1e2921 | ||
|
|
dc45c90ccd | ||
|
|
6782d65577 | ||
|
|
68ee163eb1 | ||
|
|
bc4b5d96b0 | ||
|
|
909dbb531d | ||
|
|
a2654bdc69 | ||
|
|
edcb04d1a9 | ||
|
|
29ec867ac7 | ||
|
|
22873b3c3f | ||
|
|
ede5db18d7 | ||
|
|
964b1dc58a | ||
|
|
1b00af6926 | ||
|
|
ddf1be653e | ||
|
|
cede7e5ac0 | ||
|
|
964bd8ba61 | ||
|
|
a1db219fd2 | ||
|
|
241be1801b | ||
|
|
4d9c78003a | ||
|
|
375fe19f52 | ||
|
|
1895c63e89 | ||
|
|
33a00436b1 | ||
|
|
8ad232d96a | ||
|
|
01b7721b13 | ||
|
|
46b2fe0eba | ||
|
|
89784e86bd | ||
|
|
c4b14ac40d | ||
|
|
12b519a543 | ||
|
|
f75f6623b3 | ||
|
|
43b5a0ea6a | ||
|
|
3541954df8 | ||
|
|
e0d6d35b56 | ||
|
|
8cf7719476 | ||
|
|
d2188609e4 | ||
|
|
3e180eafb4 | ||
|
|
b8a3c39984 | ||
|
|
946468dc99 | ||
|
|
340a832884 | ||
|
|
388d08e245 | ||
|
|
8a78907977 | ||
|
|
6347e275d3 | ||
|
|
c8f19e0d96 | ||
|
|
ba6d816186 | ||
|
|
849de648f0 | ||
|
|
4d18044851 | ||
|
|
d6a6734b65 | ||
|
|
10319dd641 | ||
|
|
a8b945e36e | ||
|
|
719c56e754 | ||
|
|
cbf10bdb44 | ||
|
|
ee60c428b2 | ||
|
|
a410b4981f | ||
|
|
f973009b83 | ||
|
|
e0ace35525 | ||
|
|
cf49d9f784 | ||
|
|
79d5873445 | ||
|
|
c5e3e0ee70 | ||
|
|
6976f5f3f0 | ||
|
|
1f37ac83e4 | ||
|
|
36ebddf986 | ||
|
|
b496f8d70a | ||
|
|
9351eee3f1 | ||
|
|
d412d6502d | ||
|
|
b85f81d529 | ||
|
|
4140492d56 | ||
|
|
59e133d3bc | ||
|
|
90f6d4e563 | ||
|
|
3e31ee9455 | ||
|
|
d52c90ede4 | ||
|
|
9527d0dcbd | ||
|
|
51d21b8dab | ||
|
|
601fcf40c4 | ||
|
|
d537c3d523 | ||
|
|
a65c87b584 | ||
|
|
589a5f9aeb | ||
|
|
e767655ede | ||
|
|
62295e370c | ||
|
|
f818f5ccf5 | ||
|
|
f28d22d20f | ||
|
|
80af171a35 | ||
|
|
71c54ab212 | ||
|
|
c37ff3efce | ||
|
|
dd0e67540f | ||
|
|
8db41e7701 | ||
|
|
37569aae17 | ||
|
|
ffd6385dd5 | ||
|
|
abe3e0e7a5 | ||
|
|
70b5ed7067 | ||
|
|
9d8beb85d7 | ||
|
|
296fc484ba | ||
|
|
ad900fc8f1 | ||
|
|
f60c25aacf | ||
|
|
d6cf595899 | ||
|
|
9329ed1e37 | ||
|
|
90628dc5c1 | ||
|
|
73ae197d20 | ||
|
|
15fa6b82a5 | ||
|
|
d887e7fea5 | ||
|
|
871f0e73c0 | ||
|
|
d117cb8ed5 | ||
|
|
733d7fb55f | ||
|
|
6ae3481ae9 | ||
|
|
b7efd94147 | ||
|
|
a05b6a927e | ||
|
|
30648a7858 | ||
|
|
26bce5ddc0 | ||
|
|
bd708159a1 | ||
|
|
87ce637bd2 | ||
|
|
7698b1b5fd | ||
|
|
e256d886e0 | ||
|
|
6f268e3a40 | ||
|
|
57910470a9 | ||
|
|
890b6e9a28 | ||
|
|
b09b21e939 | ||
|
|
edf47a1737 | ||
|
|
5b8a1ce821 | ||
|
|
d47869ac2f | ||
|
|
8ea7d4cf84 | ||
|
|
fc49888ba2 | ||
|
|
d0d6a4d1a1 | ||
|
|
961370c43c | ||
|
|
902f4c957a | ||
|
|
7e1a4dea6c | ||
|
|
c78ef29bda | ||
|
|
305a4ab6dd | ||
|
|
7777107d83 | ||
|
|
67c72a0e86 | ||
|
|
e7a89a4a42 | ||
|
|
6761984f2c | ||
|
|
1630eafbe3 | ||
|
|
091bf3a64c | ||
|
|
e5a189b8c6 | ||
|
|
f68b34a485 | ||
|
|
e946b39960 | ||
|
|
0711d05232 | ||
|
|
9dd5dfdde2 | ||
|
|
024aa60209 | ||
|
|
32916f9a6f | ||
|
|
b670bb8d2c | ||
|
|
1abf6547ff | ||
|
|
3739fa401e | ||
|
|
377d62999f | ||
|
|
6f1f69683d | ||
|
|
b4bb4ca397 | ||
|
|
6b4fb434da | ||
|
|
9e70f53afa | ||
|
|
81885dec63 | ||
|
|
f59f3c3c83 | ||
|
|
18c66ae7ac | ||
|
|
d01c168450 | ||
|
|
f0a4a9e3c4 | ||
|
|
78e59a9400 | ||
|
|
a8f194259b | ||
|
|
c39fdcb8ac | ||
|
|
26e46a6e18 | ||
|
|
4ad7edc35e | ||
|
|
28eacdb2bb | ||
|
|
66b826a8e1 | ||
|
|
20c8a856a2 | ||
|
|
dd20bd5486 | ||
|
|
f8077d9fc4 | ||
|
|
16dfb7b5f5 | ||
|
|
aae0e16350 | ||
|
|
741a71b490 | ||
|
|
44ee578a3a | ||
|
|
f92f34dc54 | ||
|
|
dff9c9ad22 | ||
|
|
acae7eccc4 | ||
|
|
837739d7dd | ||
|
|
2567303fd7 | ||
|
|
4939ae1a2f | ||
|
|
58607e2ca0 | ||
|
|
cb021e3a25 | ||
|
|
d61936c06d | ||
|
|
3553eb1f2a | ||
|
|
c4a9560d53 | ||
|
|
bc63b7608e | ||
|
|
c2f508e261 | ||
|
|
a07dc0a511 | ||
|
|
d8db320722 | ||
|
|
24054f905f | ||
|
|
32ca815896 | ||
|
|
fa5949e191 | ||
|
|
1c0a98a0cc | ||
|
|
9d208e8795 | ||
|
|
72fe0cd6db | ||
|
|
734590b6bd | ||
|
|
7cc231e8b9 | ||
|
|
c2bf4ef7d4 | ||
|
|
439179e37f | ||
|
|
c333cb542c | ||
|
|
6a6546db8d | ||
|
|
a6d22a5a4b | ||
|
|
da5f94e62c | ||
|
|
1d886f2995 | ||
|
|
a6b6aa570e | ||
|
|
32b26a09c2 | ||
|
|
8fcaa4abf2 | ||
|
|
18e1f08156 | ||
|
|
bf5cc63bc5 | ||
|
|
271a94e0c7 | ||
|
|
c3f34db81e | ||
|
|
b7d8a769db | ||
|
|
c05af3b9b3 | ||
|
|
3e37a60635 | ||
|
|
9d47b6843f | ||
|
|
12ffac1f06 | ||
|
|
7991b4ab25 | ||
|
|
984fb702e5 | ||
|
|
019b88cedf | ||
|
|
55932cf3c7 | ||
|
|
1ff2c9bbd9 | ||
|
|
72235b0fd4 | ||
|
|
b8d391024a | ||
|
|
598bec0eab | ||
|
|
709bf7b246 | ||
|
|
8251c8f259 | ||
|
|
9c0bde0b29 | ||
|
|
2cbce963b7 | ||
|
|
64e6e686e0 | ||
|
|
dad3458ece | ||
|
|
e727e7adb6 | ||
|
|
af1c952700 | ||
|
|
ce2f7112a3 | ||
|
|
4650ed01eb | ||
|
|
0e2c73a36d | ||
|
|
139fed40dd | ||
|
|
ee88161808 | ||
|
|
04d54e8d82 | ||
|
|
c6c0d3d420 | ||
|
|
f4d70068b1 | ||
|
|
3fe5a8d0d6 | ||
|
|
29a124900c | ||
|
|
6670407c9c | ||
|
|
641b6067cd | ||
|
|
b1d61fa90b | ||
|
|
0c93cf2600 | ||
|
|
53753c0932 | ||
|
|
029cfaf1f8 | ||
|
|
66edc39ccb | ||
|
|
3f42f785ac | ||
|
|
fd4d32351b | ||
|
|
75d3895e84 | ||
|
|
d022d7f09e | ||
|
|
88b414b9a3 | ||
|
|
47c441ba40 | ||
|
|
7105e38544 | ||
|
|
4b0cdf9123 | ||
|
|
1c9fdc79c0 | ||
|
|
90d04990ca | ||
|
|
aa001ed2dc | ||
|
|
ce1c37dbca | ||
|
|
3a3bc568b3 | ||
|
|
b048b2684b | ||
|
|
9a1a81925e | ||
|
|
e38f3cc12c | ||
|
|
4fb356b19b | ||
|
|
cb6b8a9670 | ||
|
|
efc5237265 | ||
|
|
cb999af653 | ||
|
|
8d7d2fb1f1 | ||
|
|
eaff060bf9 | ||
|
|
6cc5adcd25 | ||
|
|
233625db20 | ||
|
|
96093984e4 | ||
|
|
ea0af4dc43 | ||
|
|
8fa5241a13 | ||
|
|
e38eb43955 | ||
|
|
3b46e88734 | ||
|
|
d6a58f33f3 | ||
|
|
d1c15bd0b7 | ||
|
|
1dc6ebbffc | ||
|
|
ee1e07f3d7 | ||
|
|
9e7b7fbb3a | ||
|
|
04fd330733 | ||
|
|
361b671799 | ||
|
|
b8f8ec8aa3 | ||
|
|
05d570d250 | ||
|
|
e8f11286dc | ||
|
|
48bc5637a0 | ||
|
|
7bfbe264c7 | ||
|
|
e37876afec | ||
|
|
02153356de | ||
|
|
a686c5f9aa | ||
|
|
ab7eb0a293 | ||
|
|
738485a655 | ||
|
|
0c751ea14f | ||
|
|
01f249d484 | ||
|
|
53f9f35233 | ||
|
|
9a890c7625 | ||
|
|
2bc23c468e | ||
|
|
6e1cde815c | ||
|
|
d1f1ed8957 | ||
|
|
a1a23b66c8 | ||
|
|
b4a8d0d783 | ||
|
|
fcc76809e3 | ||
|
|
1eae5cf272 | ||
|
|
ad1ee0f3dc | ||
|
|
0f99bfe7c4 | ||
|
|
dbba49540e | ||
|
|
5440ef7eb2 | ||
|
|
8d63b98212 | ||
|
|
d4b1e8f1be | ||
|
|
47d2a661bc | ||
|
|
7f46bcef61 | ||
|
|
4fd90b5d52 | ||
|
|
28c206da18 | ||
|
|
5e58735bb3 | ||
|
|
c06c391461 | ||
|
|
31c7144fff | ||
|
|
9175d26b3b | ||
|
|
f1d6f70cd2 | ||
|
|
b27b809ea9 | ||
|
|
1ec837089e | ||
|
|
089267837d | ||
|
|
c851322826 | ||
|
|
1b12db92ab | ||
|
|
6c0775ba59 | ||
|
|
0d381e4c91 | ||
|
|
7ccda7e99b | ||
|
|
567de2e115 | ||
|
|
cf49a9a6e7 | ||
|
|
41493993a2 | ||
|
|
1d10f85c28 | ||
|
|
d3d84524f5 | ||
|
|
cc04221516 | ||
|
|
f9bc50e262 | ||
|
|
9545e409d4 | ||
|
|
07bd8afc4a | ||
|
|
9ac8d7ec49 | ||
|
|
b4bdc8adee | ||
|
|
8299c04fc6 | ||
|
|
0b1b0a3f80 | ||
|
|
2773a621a2 | ||
|
|
2edae22a65 | ||
|
|
c06cf44fd7 | ||
|
|
7288e5d4a4 | ||
|
|
b62f501745 | ||
|
|
c199a51eeb | ||
|
|
b60d222c81 | ||
|
|
fb57fb714a | ||
|
|
bc390088b3 | ||
|
|
532e2ec8e1 | ||
|
|
6f4b6cec7e | ||
|
|
0b972c5b4b | ||
|
|
45e126bf07 | ||
|
|
318f28affd | ||
|
|
2214954c51 | ||
|
|
ec515f4fb5 | ||
|
|
3fb75707e7 | ||
|
|
c1850e0e20 | ||
|
|
b8d87e2d5a | ||
|
|
1f53fd85b4 | ||
|
|
564c93ee21 | ||
|
|
6769786241 | ||
|
|
0946e860f1 | ||
|
|
33366469a3 | ||
|
|
7d29efeb33 | ||
|
|
f0e9af1cf5 | ||
|
|
aec8e1db2d | ||
|
|
170fb64bfc | ||
|
|
b134c4b778 | ||
|
|
6a06f7b2ea | ||
|
|
5e0922395c | ||
|
|
514927c0b3 | ||
|
|
63f05e12ca | ||
|
|
b87d70c71c | ||
|
|
3f403d34a4 | ||
|
|
5d84e33be7 | ||
|
|
00a37b6289 | ||
|
|
963393e3d9 | ||
|
|
53a878bf61 | ||
|
|
5eac48094b | ||
|
|
90e8e02b0c | ||
|
|
0dcfe027b0 | ||
|
|
7d73c7fca9 | ||
|
|
d37862b123 | ||
|
|
df9b338279 | ||
|
|
97c7d176f0 | ||
|
|
f966b1ae6a | ||
|
|
8bc08d7716 | ||
|
|
ef4c0f961d | ||
|
|
42840827f8 | ||
|
|
13b6b9de60 | ||
|
|
74a21bab1a | ||
|
|
d1c7770949 | ||
|
|
66edf22ea3 | ||
|
|
8fffd10280 | ||
|
|
446dbde836 | ||
|
|
ef84a5a76a | ||
|
|
25a63956f3 | ||
|
|
eddd16283d | ||
|
|
c5efe7a17b | ||
|
|
7075b8b973 | ||
|
|
3f5957d30e | ||
|
|
bc29957d1e | ||
|
|
289835039a | ||
|
|
b1e08ef231 | ||
|
|
8a463767bf | ||
|
|
c598e4d289 | ||
|
|
a7624a2bf9 | ||
|
|
d20fcbd845 | ||
|
|
5d664855de | ||
|
|
8366cf0873 | ||
|
|
a41789a746 | ||
|
|
cde3251dd8 | ||
|
|
7c394bf735 | ||
|
|
76e0345609 | ||
|
|
d8820fa185 | ||
|
|
b6502693e4 | ||
|
|
f7e5402966 | ||
|
|
1e6a825412 | ||
|
|
c23065aacd | ||
|
|
04f5ba67a2 | ||
|
|
b87fa6715b | ||
|
|
f6f3298e03 | ||
|
|
6bfd781947 | ||
|
|
aff6331211 | ||
|
|
d547c39a16 | ||
|
|
3cea422365 | ||
|
|
ac39606386 | ||
|
|
12ae44d563 | ||
|
|
57b37d9005 | ||
|
|
9d5cc07567 | ||
|
|
75d4d767c6 | ||
|
|
0be4b86230 | ||
|
|
784d15b012 | ||
|
|
00f6045b12 | ||
|
|
b26f842de1 | ||
|
|
0ab2406db9 | ||
|
|
bf7e45439b | ||
|
|
0652273fe1 | ||
|
|
5927a54208 | ||
|
|
b46db59948 | ||
|
|
23b5a4729f | ||
|
|
8ae47bd490 | ||
|
|
1796d20399 | ||
|
|
5ac2447b85 | ||
|
|
db445c3a8e | ||
|
|
de2914978d | ||
|
|
09812a7bfc | ||
|
|
2eb3b541f4 | ||
|
|
e9791ff92c | ||
|
|
88516546fa | ||
|
|
9c75c55fa4 | ||
|
|
b9741e87bd | ||
|
|
c555478b54 | ||
|
|
3718372288 | ||
|
|
390b41bc26 | ||
|
|
530c5a64fb | ||
|
|
d285aaedc9 | ||
|
|
453fe18d7f | ||
|
|
5fae5cd308 | ||
|
|
7d7e225823 | ||
|
|
19f404799d | ||
|
|
3e4652dca3 | ||
|
|
45b08de874 | ||
|
|
310e26dda9 | ||
|
|
f4eb54b835 | ||
|
|
3968c12947 | ||
|
|
21c97d255f | ||
|
|
eb1053607a | ||
|
|
de7198e9dc | ||
|
|
0f30f47249 | ||
|
|
6b2ad8ebc8 | ||
|
|
1f302144ef | ||
|
|
d04c7a6ae4 | ||
|
|
9ca2cda8c7 | ||
|
|
1fd06ecbf9 | ||
|
|
97baad4cb1 | ||
|
|
fbd93ecf0d | ||
|
|
e941ccea92 | ||
|
|
d692483bc3 | ||
|
|
95cfbd0fff | ||
|
|
b3d1ed9e65 | ||
|
|
fe2b8c9fee | ||
|
|
2d7deef4e2 | ||
|
|
b4a70b02e3 | ||
|
|
c5c2364ed4 | ||
|
|
efcc4291a3 | ||
|
|
6ea6ee264d | ||
|
|
2865ba7632 | ||
|
|
2bed668d31 | ||
|
|
9dc24860f3 | ||
|
|
f01377b3bc | ||
|
|
7443dfac4c | ||
|
|
e6408e187c | ||
|
|
a02d282d3e | ||
|
|
f778f53744 | ||
|
|
95ea93564e | ||
|
|
d51029e86c | ||
|
|
1016699c94 | ||
|
|
63f63955e7 | ||
|
|
37be9fda9f | ||
|
|
0756133a7e | ||
|
|
83c5ab318b | ||
|
|
0c28957016 | ||
|
|
959084040d | ||
|
|
8a428c6936 | ||
|
|
48da23226c | ||
|
|
5f0c6e5102 | ||
|
|
29f1c6f50e | ||
|
|
4d187419ac | ||
|
|
58998f9cab | ||
|
|
cdc5ca8854 | ||
|
|
44e1e41266 | ||
|
|
33fda8605a | ||
|
|
5f9ed69299 | ||
|
|
7f1baea3b0 | ||
|
|
f75026e4b2 | ||
|
|
ce7a1a9c67 | ||
|
|
a62056fb19 | ||
|
|
f3434a8155 | ||
|
|
4e023ef517 | ||
|
|
97b80cb930 | ||
|
|
525b4e6a53 | ||
|
|
054eaf65b8 | ||
|
|
48fa796ab1 | ||
|
|
1873e022cc | ||
|
|
35a8062b58 | ||
|
|
636248ad67 | ||
|
|
4511c54fad | ||
|
|
7f3970541b | ||
|
|
4040f4d151 | ||
|
|
887d374c53 | ||
|
|
be4b87155d | ||
|
|
b987a7da4c | ||
|
|
7153fe5ad2 | ||
|
|
ccd8ba44d9 | ||
|
|
e7ef0f7a6c | ||
|
|
400b58c0e9 | ||
|
|
5257496214 | ||
|
|
e1bfe4a3ce | ||
|
|
f31cce8ec2 | ||
|
|
169ebfa72c | ||
|
|
7cace52ab5 | ||
|
|
95b93c60d9 | ||
|
|
5af1dcb8b1 | ||
|
|
6a61774fb7 | ||
|
|
ccbaca24f1 | ||
|
|
07b6048dc5 | ||
|
|
60dd34d066 | ||
|
|
28451d1e14 | ||
|
|
db95b6381f | ||
|
|
6b14c9bea4 | ||
|
|
742adc00fe | ||
|
|
52897cc16c | ||
|
|
c950568f1b | ||
|
|
845d7ff188 | ||
|
|
3bd8658da6 | ||
|
|
336a38081a | ||
|
|
01c2131436 | ||
|
|
c274231544 | ||
|
|
4a2864701c | ||
|
|
76ede10e0a | ||
|
|
274e01bb75 | ||
|
|
d75f763c99 | ||
|
|
5bc985663c | ||
|
|
df9e2e853f | ||
|
|
b4828a6f0a | ||
|
|
e99dd749a0 | ||
|
|
10ce7178c0 | ||
|
|
5c6a66eaf5 | ||
|
|
36d30bc985 | ||
|
|
a5152b82e9 | ||
|
|
e9af8a2595 | ||
|
|
84b5b60d49 | ||
|
|
8f60f42be3 | ||
|
|
583344138a | ||
|
|
016d021d5a | ||
|
|
115dc4bfa4 | ||
|
|
5b83febb23 | ||
|
|
c9d5c50402 | ||
|
|
fc839d2983 | ||
|
|
3bce96bbd5 | ||
|
|
6279be073b | ||
|
|
ea37132ce4 | ||
|
|
70eecd5289 | ||
|
|
380d03257f | ||
|
|
006de6da14 | ||
|
|
10aa80e8f5 | ||
|
|
013439af6d | ||
|
|
3408961155 | ||
|
|
f3b4a8d055 | ||
|
|
104af7e86f | ||
|
|
be39fbeff6 | ||
|
|
4109045fa4 | ||
|
|
90fd8023dd | ||
|
|
f67ad9c061 | ||
|
|
525e2bafee | ||
|
|
b65a9abf8e | ||
|
|
fec94aa53a | ||
|
|
3d4b345728 | ||
|
|
579975f08d | ||
|
|
3707b39fef | ||
|
|
f07387225b | ||
|
|
2648fb1bb1 | ||
|
|
d34715b4ba | ||
|
|
63af50bf98 | ||
|
|
456550c1d4 | ||
|
|
8174b88ec3 | ||
|
|
3233973748 | ||
|
|
bdfb1cf33e | ||
|
|
1c5fcd59e7 | ||
|
|
5cc960527e | ||
|
|
762c53fb8d | ||
|
|
ff20e67d07 | ||
|
|
c0cea013d1 | ||
|
|
5526bbba64 | ||
|
|
f0aa96ea8c | ||
|
|
e73007c398 | ||
|
|
fdc459ec5b | ||
|
|
bdb523ece1 | ||
|
|
164a9479ad | ||
|
|
e18adc781f | ||
|
|
33d89c2739 | ||
|
|
7cc9ab9083 | ||
|
|
4b4b7dc169 | ||
|
|
71ad5c5f05 | ||
|
|
39368bb5cb | ||
|
|
7a587ee8d1 | ||
|
|
77346527f3 | ||
|
|
1eba5833d5 | ||
|
|
83a747794e | ||
|
|
3e16d1da46 | ||
|
|
ae1860e859 | ||
|
|
2ebc8fdf2a | ||
|
|
be4023be66 | ||
|
|
7f4ad76298 | ||
|
|
0cbfaf98f3 | ||
|
|
631124e658 | ||
|
|
1685ee1ecb | ||
|
|
9b4d11f220 | ||
|
|
46a71296a9 | ||
|
|
1285588b62 | ||
|
|
d96392f65e | ||
|
|
d1c5a736ae | ||
|
|
6b1e038c5c | ||
|
|
eaab1aae28 | ||
|
|
31030343a2 | ||
|
|
325ca03a13 | ||
|
|
dea8e63df2 | ||
|
|
58421fd31a | ||
|
|
b961c96862 | ||
|
|
2d23c1b0f3 | ||
|
|
06952c224b | ||
|
|
2ea492c965 | ||
|
|
dbf84f6879 | ||
|
|
0fa3d6c462 | ||
|
|
d57f7aa03f | ||
|
|
d64f9f5401 | ||
|
|
a3029afc41 | ||
|
|
6a7d904fae | ||
|
|
d4043d3f86 | ||
|
|
b4902a4f58 | ||
|
|
ffe402f201 | ||
|
|
09cc7da282 | ||
|
|
2d2dad41f4 | ||
|
|
5f7c0a86dd | ||
|
|
fc1c631c98 | ||
|
|
89bdafacb8 | ||
|
|
73b6b3f129 | ||
|
|
b2a495f593 | ||
|
|
65ee904377 | ||
|
|
13f59230b5 | ||
|
|
36d2a0de1e | ||
|
|
a4db9fc8e5 | ||
|
|
9dae5ef83b | ||
|
|
e8842a740c | ||
|
|
0d3807ad09 | ||
|
|
5c27a249b7 | ||
|
|
7e41860b28 | ||
|
|
43ff92bbe7 | ||
|
|
28adc7e563 | ||
|
|
9788411995 | ||
|
|
0c9e8cc50e | ||
|
|
34d572c523 | ||
|
|
011b496b3f | ||
|
|
12b906eac6 | ||
|
|
20937d05c3 | ||
|
|
4943d37ccf | ||
|
|
3a8fd215de | ||
|
|
87572e8922 | ||
|
|
f1eedc7a01 | ||
|
|
b79e48dd77 | ||
|
|
18872194af | ||
|
|
bafd7ba282 | ||
|
|
b186481181 | ||
|
|
09ca6d11ad | ||
|
|
e68e4e786d | ||
|
|
ee638254c3 | ||
|
|
1e678905c4 | ||
|
|
10804c4b25 | ||
|
|
4bf9b4d41b | ||
|
|
1161872324 | ||
|
|
98cb570896 | ||
|
|
ed4ee3b58e | ||
|
|
066048f4de | ||
|
|
4b6b91c08b | ||
|
|
2980523a5b | ||
|
|
f2f9c043bf | ||
|
|
5d59cfd2c9 | ||
|
|
f94474e24f | ||
|
|
a63fc6d9ba | ||
|
|
076adeef80 | ||
|
|
a0e756317c | ||
|
|
252cb5f2f3 | ||
|
|
64288b4914 | ||
|
|
9ca6c6a315 | ||
|
|
3651ab5c0c | ||
|
|
b3f15e1ddc | ||
|
|
da2a5f72bd | ||
|
|
591e6b68e0 | ||
|
|
0119abdcdd | ||
|
|
e57ca15330 | ||
|
|
f53376cea1 | ||
|
|
4f1c463bdd | ||
|
|
6643a3d937 | ||
|
|
da8cb40242 | ||
|
|
4c6d304e60 | ||
|
|
99d3ef42e9 | ||
|
|
e2289dc2a0 | ||
|
|
9b4f50cde9 | ||
|
|
fe64bd9dbb | ||
|
|
0991264c8c | ||
|
|
3b608ad544 | ||
|
|
3f1a379908 | ||
|
|
61a67dae29 | ||
|
|
609aefd808 | ||
|
|
191a2495a5 | ||
|
|
a235b760dc | ||
|
|
e4eb3c23a2 | ||
|
|
12582e963d | ||
|
|
d5074871c7 | ||
|
|
e0d024ac95 | ||
|
|
7a756cacb9 | ||
|
|
3c1da423fa | ||
|
|
38dfaa1caa | ||
|
|
a050cff50f | ||
|
|
93c1b37aab | ||
|
|
01d4226c4a | ||
|
|
fc6032d3b7 | ||
|
|
43839d1090 | ||
|
|
b3632584c3 | ||
|
|
e9257580cd | ||
|
|
e3cc6309ea | ||
|
|
17fd625f7f | ||
|
|
d1ecfd8657 | ||
|
|
4aa3cfad40 | ||
|
|
3bcb697662 | ||
|
|
88318b73e4 | ||
|
|
2f7e202f40 | ||
|
|
310239e707 | ||
|
|
4de75373dd | ||
|
|
c0d329e6d8 | ||
|
|
8a0840d35b | ||
|
|
f9bb9ef33e | ||
|
|
acb2a5d2b0 | ||
|
|
63ef11c708 | ||
|
|
d70bbfb5d0 | ||
|
|
97d60ac98d | ||
|
|
8f1f5d33fd | ||
|
|
d65c85c19f | ||
|
|
22d893fc1e | ||
|
|
806d2f6a4a | ||
|
|
fc3baa28d6 | ||
|
|
eba45e6207 | ||
|
|
272fd3edc3 | ||
|
|
5ad8b33aa7 | ||
|
|
cacd14fcf8 | ||
|
|
859e4749ae | ||
|
|
a5842a41b2 | ||
|
|
fb275d9537 | ||
|
|
88f7b7e786 | ||
|
|
30402effa9 | ||
|
|
7d96623f06 | ||
|
|
398706246e | ||
|
|
6628fc02f2 | ||
|
|
e2fa7f59a1 | ||
|
|
d5b7dc0acc | ||
|
|
e4d874cc69 | ||
|
|
80a0abeead | ||
|
|
0df2d46ca7 | ||
|
|
07f542b4d7 | ||
|
|
7db3e8556a | ||
|
|
dc03e67b81 | ||
|
|
e587324b81 | ||
|
|
65a66492f4 | ||
|
|
17602d7065 | ||
|
|
ae56261961 | ||
|
|
c4f57608d0 | ||
|
|
753d1104ef | ||
|
|
880652f5d4 | ||
|
|
54c81d6bb2 | ||
|
|
2bf43eae24 | ||
|
|
58961d23bb | ||
|
|
6044ade373 | ||
|
|
da1c96c6fd | ||
|
|
5bbb474db6 | ||
|
|
a0c909914d | ||
|
|
170e56b34a | ||
|
|
de43569fa2 | ||
|
|
aa6b701b77 | ||
|
|
d69eb27557 | ||
|
|
0ca57d6a09 | ||
|
|
4c104d55cb | ||
|
|
8a8215fabe | ||
|
|
4badeafb98 | ||
|
|
7cb79bec49 | ||
|
|
8da0da02d9 | ||
|
|
efef260764 | ||
|
|
a56991d081 | ||
|
|
f0196540ab | ||
|
|
426b15313e | ||
|
|
11fc55d679 | ||
|
|
de1691665f | ||
|
|
b1f93b40ae | ||
|
|
5e58251026 | ||
|
|
4f4091a9bd | ||
|
|
e9fb41fdc8 | ||
|
|
6b803656b2 | ||
|
|
829741e2ac | ||
|
|
94c40909cc | ||
|
|
95dab16e6e | ||
|
|
c049413b47 | ||
|
|
2d45f95501 | ||
|
|
3cfc76b635 | ||
|
|
d88874845c | ||
|
|
5e38c1c8fe | ||
|
|
ae7ebeedd1 | ||
|
|
652b657809 | ||
|
|
62a6e0da1d | ||
|
|
0d0d48d9f6 | ||
|
|
ab5957f1e9 | ||
|
|
463ba23003 | ||
|
|
ccad6e7e1a | ||
|
|
aa165b5e17 | ||
|
|
f06e87377c | ||
|
|
4c3bf9fc7a | ||
|
|
253ed78cc6 | ||
|
|
4860d833c7 | ||
|
|
450d5c1a59 | ||
|
|
88fcda2c99 | ||
|
|
00db953c9f | ||
|
|
a0df4829a8 | ||
|
|
b0e1f12c22 | ||
|
|
ee56155ec4 | ||
|
|
16d7c6a933 | ||
|
|
f7a06c1da9 | ||
|
|
4c8086977a | ||
|
|
b1f088e5fa | ||
|
|
1247c789aa | ||
|
|
749038c76d | ||
|
|
0a052494c4 | ||
|
|
90fa83a5cf | ||
|
|
4eaff892c1 | ||
|
|
f368f75209 | ||
|
|
04048b13ed | ||
|
|
5acc33c751 | ||
|
|
b449be89a7 | ||
|
|
dac019290d | ||
|
|
bdc424e39d | ||
|
|
10193a2796 | ||
|
|
2c9a12e941 | ||
|
|
8ba6c40f0c | ||
|
|
bbfeb49cdf | ||
|
|
f61e1cb36d | ||
|
|
4a3e2c3611 | ||
|
|
81faec508c | ||
|
|
9966ca2e85 | ||
|
|
35c26f9ee5 | ||
|
|
b5e29771ab | ||
|
|
f5f09d3640 | ||
|
|
5a531b7948 | ||
|
|
f716a3a73b | ||
|
|
ce8c8c8eea | ||
|
|
fc48fda7e5 | ||
|
|
78936c5ce8 | ||
|
|
5d0efce278 | ||
|
|
0c17a0b4f2 | ||
|
|
3f396a7c52 | ||
|
|
8697f8f91f | ||
|
|
06c67685f1 | ||
|
|
dc2e7de9e5 | ||
|
|
db1dbe7a27 | ||
|
|
d6bbb94be5 | ||
|
|
e3b4c0aee3 | ||
|
|
a1fbe152bb | ||
|
|
9d28ff9b23 | ||
|
|
43f0ddd25d | ||
|
|
7a28b00d75 | ||
|
|
32e29862f2 | ||
|
|
6c5c38f5a7 | ||
|
|
2da7854b24 | ||
|
|
6d0c5ab2d5 | ||
|
|
9398deeabc | ||
|
|
bf63d2e844 | ||
|
|
b808592fb3 | ||
|
|
e2296a631b | ||
|
|
e20555d4bc | ||
|
|
b89e2dcd3c | ||
|
|
165d11b2ca | ||
|
|
d4046c0acf | ||
|
|
88498695ac | ||
|
|
354a1c23b0 | ||
|
|
34550246f4 | ||
|
|
db1cc846dc | ||
|
|
74484bcbdf | ||
|
|
d5ecf8ce16 | ||
|
|
b1ffb1d4a4 | ||
|
|
451e1122a7 | ||
|
|
10dcf32f3c | ||
|
|
7f1477b26d | ||
|
|
33b68c09d3 | ||
|
|
7ec48ca845 | ||
|
|
5c92cef983 | ||
|
|
75eba466c6 | ||
|
|
ad30737119 | ||
|
|
8e0bde3071 | ||
|
|
7d641427d2 | ||
|
|
3b62beed26 | ||
|
|
2d3cf68261 | ||
|
|
7d6080d13f | ||
|
|
e3eefeb3fe | ||
|
|
f10dddadd6 | ||
|
|
d166112917 | ||
|
|
8ed5c1bedf | ||
|
|
4489076fac | ||
|
|
bdc33cd421 | ||
|
|
889dae2955 | ||
|
|
9ff21b68e4 | ||
|
|
a69a7009f8 | ||
|
|
d413fac4cb | ||
|
|
246ecd8607 | ||
|
|
22105af720 | ||
|
|
880c4d2f48 | ||
|
|
443f489152 | ||
|
|
39fdfdfd8c | ||
|
|
96dccca475 | ||
|
|
948a3c6d08 | ||
|
|
dc13d5d26b | ||
|
|
aae714db6b | ||
|
|
a7c9673bcf | ||
|
|
3d06775ddc | ||
|
|
48beea3884 | ||
|
|
958d3f6094 | ||
|
|
08f24fb272 | ||
|
|
07d57e1a64 | ||
|
|
cd7711bdfe | ||
|
|
433ffa05a5 | ||
|
|
046b21b907 | ||
|
|
c32183eb70 | ||
|
|
73b11045f2 | ||
|
|
57ce3fa587 | ||
|
|
a26620da38 | ||
|
|
86b8099eb9 | ||
|
|
c8e9a100a6 | ||
|
|
a287f028d1 | ||
|
|
cf50fb3568 | ||
|
|
4c8193876f | ||
|
|
158bc1eb2a | ||
|
|
3f42e5f702 | ||
|
|
75633817a7 | ||
|
|
83b00fce3e | ||
|
|
38befb53ad | ||
|
|
d0b5c4de68 | ||
|
|
1b68845b00 | ||
|
|
a7bc72540d | ||
|
|
27ac7481f9 | ||
|
|
9bc36be513 | ||
|
|
e62e35bc88 | ||
|
|
bd80ced9b2 | ||
|
|
bb2f2e5e54 | ||
|
|
b1eb6711b7 | ||
|
|
da0ffa5e56 | ||
|
|
68ef312233 | ||
|
|
9fefadca24 | ||
|
|
e14b14b88c | ||
|
|
d5bfb7257e | ||
|
|
8282f3b59c | ||
|
|
dbf0c84f0b | ||
|
|
a5977b993a | ||
|
|
27df3ae876 | ||
|
|
a49d07cf01 | ||
|
|
28f343ac50 | ||
|
|
4297a39d03 | ||
|
|
bd996e441c | ||
|
|
086a89fad6 | ||
|
|
70ac38e66c | ||
|
|
d990d2ad86 | ||
|
|
56db31ca43 | ||
|
|
b902e2d30b | ||
|
|
d2bab32b0e | ||
|
|
b2d726051b | ||
|
|
8e25667f87 | ||
|
|
9b5c4c50e7 | ||
|
|
d2ce70a673 | ||
|
|
9db0fc4ee4 | ||
|
|
9ed830bb81 | ||
|
|
4e42d9ed03 | ||
|
|
4c93bc3599 | ||
|
|
7c817802a8 | ||
|
|
de90b592fb | ||
|
|
b9d0cc2e28 | ||
|
|
0ec00fe57f | ||
|
|
80931e1cb4 | ||
|
|
cc02e96a13 | ||
|
|
51ec91dd16 | ||
|
|
916a92c3d8 | ||
|
|
5431bfdc29 | ||
|
|
43b5b4f5a4 | ||
|
|
f342e06ef0 | ||
|
|
81bb87f4cd | ||
|
|
c4b97fadcc | ||
|
|
05f6ba7297 | ||
|
|
c62b8a5d4f | ||
|
|
83dab30ecf | ||
|
|
24b08a332d | ||
|
|
70ccb3022a | ||
|
|
8019b90b8a | ||
|
|
5f12ff6178 | ||
|
|
6e20e48489 | ||
|
|
f29a72235c | ||
|
|
e25d499eeb | ||
|
|
9cae339546 | ||
|
|
a049af6262 | ||
|
|
a402f50f9b | ||
|
|
9f89ea9be6 | ||
|
|
e538aacf9d | ||
|
|
968c609697 | ||
|
|
c11cfa0a62 | ||
|
|
074f4677d5 | ||
|
|
9ea5c03371 | ||
|
|
22c0ff3cf5 | ||
|
|
3ced981d28 | ||
|
|
299080f590 | ||
|
|
a407771eaf | ||
|
|
d26a6de759 | ||
|
|
9baad56197 | ||
|
|
a589e2ecf3 | ||
|
|
d7029871b1 | ||
|
|
b80a505be5 | ||
|
|
412a25462e | ||
|
|
9a8408a092 | ||
|
|
86a9181e9b | ||
|
|
9969286224 | ||
|
|
ef49aa7e08 | ||
|
|
acdb497b80 | ||
|
|
4d8faeb826 | ||
|
|
6e0dfdb16f | ||
|
|
754480a9b6 | ||
|
|
15681ddca9 | ||
|
|
3c8d424a43 | ||
|
|
7d7eb3d1cd | ||
|
|
8500339ba6 | ||
|
|
06ee05026b | ||
|
|
ddefb4e987 | ||
|
|
62d1fc7ed3 | ||
|
|
f3b99b3940 | ||
|
|
97c11c18d0 | ||
|
|
93a909551f | ||
|
|
ea52eb78d9 | ||
|
|
fdd698dade | ||
|
|
173ccf6861 | ||
|
|
a5c3db6303 | ||
|
|
3ad7097c8a | ||
|
|
8e01b6db48 | ||
|
|
67607eba8b | ||
|
|
6e7a71d01a | ||
|
|
ff69a82b57 | ||
|
|
df1e50e599 | ||
|
|
6370f0cb95 | ||
|
|
80784bb8f1 | ||
|
|
40dcd6ec99 | ||
|
|
06f2d65500 | ||
|
|
98d3c299ff | ||
|
|
46da3a34a0 | ||
|
|
f33f84d2f2 | ||
|
|
a785a43ef3 | ||
|
|
b0911c6d70 | ||
|
|
81a0e9e8c7 | ||
|
|
06d33a45f5 | ||
|
|
cfb8deac56 | ||
|
|
9544ab2e02 | ||
|
|
318fe4a5dc | ||
|
|
5597183391 | ||
|
|
05c60d9a59 | ||
|
|
f01eea33e9 | ||
|
|
9992c367bf | ||
|
|
d275a23a81 | ||
|
|
14ddd7c196 | ||
|
|
0815b20b76 | ||
|
|
cffdb06181 | ||
|
|
837388ae4e | ||
|
|
cbd2bdd4c5 | ||
|
|
f34ca3a5ca | ||
|
|
4898297cce | ||
|
|
ffcc2aa2af | ||
|
|
158fb8d31c | ||
|
|
07714c67cb | ||
|
|
f12e502c61 | ||
|
|
2fdf8d5dc3 | ||
|
|
28ec7a1e54 | ||
|
|
24cb2e6450 | ||
|
|
915b022901 | ||
|
|
4a623c1891 | ||
|
|
7508161c39 | ||
|
|
d33861ccb4 | ||
|
|
572b2575c5 | ||
|
|
d99190b166 | ||
|
|
bc91b03276 | ||
|
|
9ba893c06c | ||
|
|
27e51f1bcb | ||
|
|
e3a26483e8 | ||
|
|
33a4fd6fbe | ||
|
|
b34b359860 | ||
|
|
8b9491823d | ||
|
|
b8b6e5266f | ||
|
|
5f80c1ac2a | ||
|
|
3af7e815d0 | ||
|
|
714afe35a1 | ||
|
|
b0a8f585c3 | ||
|
|
1a2918082d | ||
|
|
22e4dfa534 | ||
|
|
41eb850b3d | ||
|
|
3a50171d19 | ||
|
|
6c9e0ff974 | ||
|
|
644e5164b1 | ||
|
|
4fefa9f2f0 | ||
|
|
4c793e0ee6 | ||
|
|
68a7de41ae | ||
|
|
94c8bc1de9 | ||
|
|
8fb0373f82 | ||
|
|
d567dc3769 | ||
|
|
ba21554c5f | ||
|
|
e37bb3ac8a | ||
|
|
79845f0dfd | ||
|
|
eb33a5a5df | ||
|
|
adbe9c7be1 | ||
|
|
b19583e7d3 | ||
|
|
1c8c0b2915 | ||
|
|
bee1aa00f1 | ||
|
|
077b6e540a | ||
|
|
e8b03545bb | ||
|
|
70c59eab4a | ||
|
|
3c677543e0 | ||
|
|
c455ef2c62 | ||
|
|
032d0992d6 | ||
|
|
67837a47ac | ||
|
|
32e3c4e029 | ||
|
|
76fcb7a06e | ||
|
|
149a2188e2 | ||
|
|
08e7caea6b | ||
|
|
e330ebc8c9 | ||
|
|
388a08e13a | ||
|
|
9ba9ef1cbf | ||
|
|
fac004b774 | ||
|
|
8cd3f28734 | ||
|
|
dcd23fcf75 | ||
|
|
1162485c2c | ||
|
|
966172eac6 | ||
|
|
12fce52cd7 | ||
|
|
5ca1e2a23f | ||
|
|
98f8a61e83 | ||
|
|
2e86d7c5ab | ||
|
|
62ca12608d | ||
|
|
406aa55667 | ||
|
|
a76dce8b15 | ||
|
|
b01d453ae3 | ||
|
|
ac629404f4 | ||
|
|
3575d597f7 | ||
|
|
2affcba3b4 | ||
|
|
846c5f8762 | ||
|
|
086af712d2 | ||
|
|
2b6e39f283 | ||
|
|
472663193a | ||
|
|
879ff838ae | ||
|
|
5e9a085e39 | ||
|
|
c2b5729ebd | ||
|
|
fdce9d6a6a | ||
|
|
bfc2549289 | ||
|
|
52fd1ae73e | ||
|
|
23e167616f | ||
|
|
51ce83f20b | ||
|
|
5e5bbf4b39 | ||
|
|
cbc3a691b9 | ||
|
|
a5247d6e69 | ||
|
|
d698b82a83 | ||
|
|
91eff75288 | ||
|
|
91a9edb322 | ||
|
|
c8ddbeaa5c | ||
|
|
3634b3450d | ||
|
|
c2a5e3f5d8 | ||
|
|
db49fe85e4 | ||
|
|
567a2e9fd8 | ||
|
|
987de00e17 | ||
|
|
baeafec74a | ||
|
|
9cfa0b14d4 | ||
|
|
948ded6792 | ||
|
|
3c69619fd9 | ||
|
|
e7c4bc7f47 | ||
|
|
277ecc901b | ||
|
|
0f70c31a30 | ||
|
|
9a97a92e31 | ||
|
|
f9d452ad2c | ||
|
|
9907c12eda | ||
|
|
19533a32b5 | ||
|
|
c5a5004f9e | ||
|
|
677cdea99d | ||
|
|
4d7c0ddbce | ||
|
|
81daf10157 | ||
|
|
b3ef4e41bf | ||
|
|
9fbf149717 | ||
|
|
95cb94a039 | ||
|
|
21f7f87716 | ||
|
|
831c7e2c32 | ||
|
|
cc0d04c8b7 | ||
|
|
46be83f8f7 | ||
|
|
28560e2045 | ||
|
|
0df4824a56 | ||
|
|
dbcabc6517 | ||
|
|
69f479b67e | ||
|
|
af75696018 | ||
|
|
80b8f8740f | ||
|
|
71ab325940 | ||
|
|
653c76709a | ||
|
|
83cc1bab38 | ||
|
|
6c8588c019 | ||
|
|
5b00ed2fb2 | ||
|
|
9f66962bfb | ||
|
|
0edba74091 | ||
|
|
1003b49dd9 | ||
|
|
884ba54f96 | ||
|
|
cf2325a2da | ||
|
|
db6972638d | ||
|
|
74e04e81d5 | ||
|
|
7c5d7365c7 | ||
|
|
0dadf3d78a | ||
|
|
e341256627 | ||
|
|
5a3bd3ca67 | ||
|
|
8102e0a468 | ||
|
|
7d55179727 | ||
|
|
bc1a1d1818 | ||
|
|
a8bbb22fe8 | ||
|
|
6b489f71a1 | ||
|
|
f1db088af4 | ||
|
|
6fe12b3fb5 | ||
|
|
dacbf9b68d | ||
|
|
9f5057eac7 | ||
|
|
525cd54921 | ||
|
|
7ac94bbf5f | ||
|
|
b8ff6938df | ||
|
|
2f6c77fba2 | ||
|
|
28a6430778 | ||
|
|
6e4157da35 | ||
|
|
4f420dde05 | ||
|
|
d9601471df | ||
|
|
9941a97e37 | ||
|
|
0a64b08669 | ||
|
|
4d9d0d4548 | ||
|
|
5f6c8545c6 | ||
|
|
ddc335d65a | ||
|
|
9cbaa892d3 | ||
|
|
9531465410 | ||
|
|
c35916fad1 | ||
|
|
bf476a058e | ||
|
|
d4e815a4cb | ||
|
|
0545c4167b | ||
|
|
6838dd02c0 | ||
|
|
14c2fd1edd | ||
|
|
6e503cc79b | ||
|
|
bd4563b699 | ||
|
|
458e115490 | ||
|
|
51369adad1 | ||
|
|
f65c5fb147 | ||
|
|
4150ae7307 | ||
|
|
a87288d519 | ||
|
|
3cf9639e99 | ||
|
|
4490c3ed1a | ||
|
|
fbcb562781 | ||
|
|
b1e035f96a | ||
|
|
11c3a26c23 | ||
|
|
1fbe72b52d | ||
|
|
f4bb066737 | ||
|
|
aaac9cbeeb | ||
|
|
0e68ff6923 | ||
|
|
1c59712cbf | ||
|
|
c2cb1c9168 | ||
|
|
cc8e2e40dd | ||
|
|
e67d97d9da | ||
|
|
d74c2115fd | ||
|
|
70e7ee2d46 | ||
|
|
d11854f4e8 | ||
|
|
4bb553e015 | ||
|
|
0af9af44e5 | ||
|
|
3a0d73f740 | ||
|
|
9b9ff2622d | ||
|
|
a4858be967 | ||
|
|
6fd5623b1f | ||
|
|
66d9c7091c | ||
|
|
525a1e8140 | ||
|
|
64dc47d7e9 | ||
|
|
f3fc7bb91e | ||
|
|
028ef14cc0 | ||
|
|
3e001f9a1c | ||
|
|
33d20ac6d8 | ||
|
|
660554cc45 | ||
|
|
a455324e8c | ||
|
|
cd5e2e1148 | ||
|
|
074da4da19 | ||
|
|
e4e39d820c | ||
|
|
e5dbb214a2 | ||
|
|
91af528ff8 | ||
|
|
18c4e39ea3 | ||
|
|
bda455ce78 | ||
|
|
a07aea1ad3 | ||
|
|
18e2dbf144 | ||
|
|
564a07e62e | ||
|
|
a358135e41 | ||
|
|
6d9be15035 | ||
|
|
b740e0b78a | ||
|
|
9546949945 | ||
|
|
8ff048d055 | ||
|
|
95a1c6e7fb | ||
|
|
0b1a4a0f30 | ||
|
|
22b48e296a | ||
|
|
c696ebf53c | ||
|
|
a0686b7d2b | ||
|
|
8d94be8924 | ||
|
|
e97ac5033f | ||
|
|
44771a0049 | ||
|
|
32aae8f57a | ||
|
|
8207e23cd9 | ||
|
|
a469029698 | ||
|
|
203d866643 | ||
|
|
1488e5ec4d | ||
|
|
af66138a17 | ||
|
|
5f060d60a7 | ||
|
|
73ccbb69ea | ||
|
|
be60440b20 | ||
|
|
837efb78e6 | ||
|
|
4a62a290d8 | ||
|
|
018399cb1f | ||
|
|
646a576358 | ||
|
|
d8e19cd79a | ||
|
|
757cb0cf23 | ||
|
|
7d92ab335a | ||
|
|
46c6d6f656 | ||
|
|
46260749c1 | ||
|
|
50664fe115 | ||
|
|
c480bd94db | ||
|
|
79923a939b | ||
|
|
327b22113a | ||
|
|
12160ab539 | ||
|
|
2462ea0892 | ||
|
|
8be09eadd4 | ||
|
|
98bc96c911 | ||
|
|
b0fce6a80d | ||
|
|
53b8a21d1e | ||
|
|
1346492d72 | ||
|
|
e5bb8d7992 | ||
|
|
49594b8435 | ||
|
|
3bd37a7906 | ||
|
|
e070a85ae0 | ||
|
|
c189278e24 | ||
|
|
2a8606bd98 | ||
|
|
18ea05c837 | ||
|
|
86c3072515 | ||
|
|
fccf508dde | ||
|
|
2da21f90f4 | ||
|
|
bec7f1726f | ||
|
|
74dfb9d88d | ||
|
|
02dddfc227 | ||
|
|
545016b38f | ||
|
|
0ccceaf226 | ||
|
|
a601115650 | ||
|
|
ae6267c906 | ||
|
|
ac142694f5 | ||
|
|
69b0913315 | ||
|
|
421bacd7dc | ||
|
|
573a76eedb | ||
|
|
b7948c7f40 | ||
|
|
2647d09b8f | ||
|
|
57e919d7e5 | ||
|
|
f456aa1407 | ||
|
|
d0d62892c8 | ||
|
|
a981cfa053 | ||
|
|
55290dd1e3 | ||
|
|
9c4e255994 | ||
|
|
f9c7d5f7bc | ||
|
|
49baea5f6a | ||
|
|
6209cf3933 | ||
|
|
d170a523c3 | ||
|
|
be5040e7a8 | ||
|
|
ecbaa5bfc1 | ||
|
|
25e2af7c89 | ||
|
|
605688426d | ||
|
|
0e069f1e75 | ||
|
|
e9adbf18d3 | ||
|
|
610202097a | ||
|
|
8c2c552164 | ||
|
|
b9976cf693 | ||
|
|
3261c405bd | ||
|
|
35d3328e3e | ||
|
|
e96041d76f | ||
|
|
c2034bc0c0 | ||
|
|
e8855f7621 | ||
|
|
bdb8368e89 | ||
|
|
f160db2032 | ||
|
|
de9a32a273 | ||
|
|
6ba7422c3b | ||
|
|
5cbb0ceb80 | ||
|
|
5b29358b37 | ||
|
|
90147f3dfb | ||
|
|
72873abe05 | ||
|
|
de1810ba68 | ||
|
|
7b7c765d78 | ||
|
|
806d4660cf | ||
|
|
5ae5d364bb | ||
|
|
1af67e72d4 | ||
|
|
ed268ad683 | ||
|
|
5bdd2ca02f | ||
|
|
eb59861d4d | ||
|
|
427e46a2aa | ||
|
|
68a8649292 | ||
|
|
2aff8709a5 | ||
|
|
62c3add888 | ||
|
|
3ac878db62 | ||
|
|
c247cd8fea | ||
|
|
b6772b7280 | ||
|
|
807a3df9d1 | ||
|
|
491d60e267 | ||
|
|
4811eafd67 | ||
|
|
8dedbb9620 | ||
|
|
dd8454161f | ||
|
|
9421f2cddd | ||
|
|
d8c4f78ec1 | ||
|
|
54296da647 | ||
|
|
357102fdb5 | ||
|
|
7e15a9e181 | ||
|
|
12e0b2d6f7 | ||
|
|
11b40bf32f | ||
|
|
8d2b53373f | ||
|
|
9ecc49e592 | ||
|
|
4f34f7083b | ||
|
|
2a6df875ec | ||
|
|
51c83116a2 | ||
|
|
74435aac76 | ||
|
|
5dfdb5b5f9 | ||
|
|
ac892a3f3d | ||
|
|
1a2e99f559 | ||
|
|
e97bba0524 | ||
|
|
0538f0c524 | ||
|
|
fc3e35868d | ||
|
|
f1e0cfea1c | ||
|
|
56efef69ba | ||
|
|
668ec8a248 | ||
|
|
60912bd01c | ||
|
|
0b416e44f8 | ||
|
|
ecc4aa09d3 | ||
|
|
b921aabbed | ||
|
|
6ad8ac0b6b | ||
|
|
44e7e0e970 | ||
|
|
45820b4ce3 | ||
|
|
3a098377cb | ||
|
|
35875485ee | ||
|
|
19760be0bc | ||
|
|
b3ea33f88d | ||
|
|
5b3425a689 | ||
|
|
a3d157bde6 | ||
|
|
2c8c9264a4 | ||
|
|
0009d9b20e | ||
|
|
dd8d17232f | ||
|
|
6312b9225f | ||
|
|
68cc09fef2 | ||
|
|
0651c9de65 | ||
|
|
38261ec809 | ||
|
|
067932aebf | ||
|
|
af47511d58 | ||
|
|
36b916f27f | ||
|
|
e519811893 |
3
.ackrc
3
.ackrc
@@ -1,3 +1,6 @@
|
||||
--ignore-dir=old/
|
||||
--ignore-dir=tmp/
|
||||
--ignore-dir=vendor/
|
||||
--ignore-dir=releases/
|
||||
--ignore-dir=rpmbuild/
|
||||
--type-set=mcl:ext:mcl
|
||||
|
||||
@@ -12,8 +12,17 @@ end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.sh]
|
||||
indent_style = tab
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.mcl]
|
||||
indent_style = tab
|
||||
|
||||
[*.txtar]
|
||||
indent_style = tab
|
||||
|
||||
5
.github/FUNDING.yml
vendored
Normal file
5
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# You can add one username per supported platform and one custom link.
|
||||
custom: "https://paypal.me/purpleidea"
|
||||
github: purpleidea
|
||||
liberapay: purpleidea
|
||||
patreon: purpleidea
|
||||
9
.github/ISSUE_TEMPLATE.md
vendored
Normal file
9
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
## Versions:
|
||||
|
||||
* mgmt version (eg: `mgmt --version`):
|
||||
|
||||
* operating system/distribution (eg: `uname -a`):
|
||||
|
||||
* golang version (eg: `go version`):
|
||||
|
||||
## Description:
|
||||
47
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
47
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
## Tips:
|
||||
|
||||
* please read the style guide before submitting your patch:
|
||||
[docs/style-guide.md](../docs/style-guide.md)
|
||||
|
||||
* commit message titles must be in the form:
|
||||
|
||||
```topic: Capitalized message with no trailing period```
|
||||
|
||||
or:
|
||||
|
||||
```topic, topic2: Capitalized message with no trailing period```
|
||||
|
||||
* golang code must be formatted according to the standard, please run:
|
||||
|
||||
```
|
||||
make gofmt # formats the entire project correctly
|
||||
```
|
||||
|
||||
or format a single golang file correctly:
|
||||
|
||||
```
|
||||
gofmt -w yourcode.go
|
||||
```
|
||||
|
||||
* please rebase your patch against current git master:
|
||||
|
||||
```
|
||||
git checkout master
|
||||
git pull origin master
|
||||
git checkout your-feature
|
||||
git rebase master
|
||||
git push your-remote your-feature
|
||||
hub pull-request # or submit with the github web ui
|
||||
```
|
||||
|
||||
* after a patch review, please ping @purpleidea so we know to re-review:
|
||||
|
||||
```
|
||||
# make changes based on reviews...
|
||||
git add -p # add new changes
|
||||
git commit --amend # combine with existing commit
|
||||
git push your-remote your-feature -f
|
||||
# now ping @purpleidea in the github PR since it doesn't notify us automatically
|
||||
```
|
||||
|
||||
## Thanks for contributing to mgmt and welcome to the team!
|
||||
98
.github/settings.yml
vendored
Normal file
98
.github/settings.yml
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
# These settings are synced to GitHub by https://probot.github.io/apps/settings/
|
||||
|
||||
repository:
|
||||
# See https://developer.github.com/v3/repos/#edit for all available settings.
|
||||
|
||||
# The name of the repository. Changing this will rename the repository
|
||||
name: mgmt
|
||||
|
||||
# A short description of the repository that will show up on GitHub
|
||||
description: Next generation distributed, event-driven, parallel config management!
|
||||
|
||||
# A URL with more information about the repository
|
||||
homepage: https://purpleidea.com/tags/mgmtconfig/
|
||||
|
||||
# A comma-separated list of topics to set on the repository
|
||||
topics: golang, go, configuration-management, config-management, devops, etcd, distributed-systems, graph-theory, choreography
|
||||
|
||||
# Either `true` to make the repository private, or `false` to make it public.
|
||||
private: false
|
||||
|
||||
# Either `true` to enable issues for this repository, `false` to disable them.
|
||||
has_issues: true
|
||||
|
||||
# Either `true` to enable projects for this repository, or `false` to disable them.
|
||||
# If projects are disabled for the organization, passing `true` will cause an API error.
|
||||
has_projects: false
|
||||
|
||||
# Either `true` to enable the wiki for this repository, `false` to disable it.
|
||||
has_wiki: false
|
||||
|
||||
# Either `true` to enable downloads for this repository, `false` to disable them.
|
||||
has_downloads: true
|
||||
|
||||
# Updates the default branch for this repository.
|
||||
default_branch: master
|
||||
|
||||
# Either `true` to allow squash-merging pull requests, or `false` to prevent
|
||||
# squash-merging.
|
||||
allow_squash_merge: false
|
||||
|
||||
# Either `true` to allow merging pull requests with a merge commit, or `false`
|
||||
# to prevent merging pull requests with merge commits.
|
||||
allow_merge_commit: false
|
||||
|
||||
# Either `true` to allow rebase-merging pull requests, or `false` to prevent
|
||||
# rebase-merging.
|
||||
allow_rebase_merge: true
|
||||
|
||||
# Labels: define labels for Issues and Pull Requests (in alphabetical order)
|
||||
labels:
|
||||
- name: bug
|
||||
color: fc2929
|
||||
- name: confirmed
|
||||
color: d93f0b
|
||||
- name: design
|
||||
color: 5319e7
|
||||
- name: duplicate
|
||||
color: cccccc
|
||||
- name: enhancement
|
||||
color: 84b6eb
|
||||
- name: good first issue
|
||||
color: 7057ff
|
||||
- name: help wanted
|
||||
color: 159818
|
||||
- name: invalid
|
||||
color: e6e6e6
|
||||
- name: mgmtlove
|
||||
color: e11d21
|
||||
- name: question
|
||||
color: cc317c
|
||||
- name: needinfo
|
||||
color: fbca04
|
||||
- name: wontfix
|
||||
color: ffffff
|
||||
# - name: first-timers-only
|
||||
# # include the old name to rename an existing label
|
||||
# oldname: Help Wanted
|
||||
|
||||
# Collaborators: give specific users access to this repository.
|
||||
#collaborators:
|
||||
# - username: purpleidea
|
||||
# # Note: Only valid on organization-owned repositories.
|
||||
# # The permission to grant the collaborator. Can be one of:
|
||||
# # * `pull` - can pull, but not push to or administer this repository.
|
||||
# # * `push` - can pull and push, but not administer this repository.
|
||||
# # * `admin` - can pull, push and administer this repository.
|
||||
# permission: push
|
||||
|
||||
# - username: hubot
|
||||
# permission: pull
|
||||
|
||||
# NOTE: The APIs needed for teams are not supported yet by GitHub Apps
|
||||
# https://developer.github.com/v3/apps/available-endpoints/
|
||||
#teams:
|
||||
# - name: core
|
||||
# permission: admin
|
||||
# - name: docs
|
||||
# permission: push
|
||||
68
.github/workflows/test.yaml
vendored
Normal file
68
.github/workflows/test.yaml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
# Docs: https://help.github.com/en/articles/workflow-syntax-for-github-actions
|
||||
|
||||
# If the name is omitted, it uses the filename instead.
|
||||
#name: Test
|
||||
on:
|
||||
# Run on all pull requests.
|
||||
pull_request:
|
||||
#branches:
|
||||
#- master
|
||||
# Run on all pushes.
|
||||
push:
|
||||
# Run daily at 4am.
|
||||
schedule:
|
||||
- cron: 0 4 * * *
|
||||
|
||||
jobs:
|
||||
maketest:
|
||||
name: Test (${{ matrix.test_block }}) on ${{ matrix.os }} with golang ${{ matrix.golang_version }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
GOPATH: /home/runner/work/mgmt/mgmt/go
|
||||
strategy:
|
||||
matrix:
|
||||
# TODO: Add tip when it's supported: https://github.com/actions/setup-go/issues/21
|
||||
os:
|
||||
- ubuntu-latest
|
||||
# macos tests are currently failing in CI
|
||||
#- macos-latest
|
||||
golang_version:
|
||||
# TODO: add 1.24.x and tip
|
||||
# minimum required and latest published go_version
|
||||
- "1.23"
|
||||
test_block:
|
||||
- basic
|
||||
- shell
|
||||
- race
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
# Do not shallow fetch. The path can't be absolute, so we need to move it
|
||||
# to the expected location later.
|
||||
- name: Clone mgmt
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
path: ./go/src/github.com/purpleidea/mgmt
|
||||
|
||||
- name: Install Go ${{ matrix.golang_version }}
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.golang_version }}
|
||||
|
||||
# Install & configure ruby, fixes gem permissions error
|
||||
- name: Install Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: head
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: ./go/src/github.com/purpleidea/mgmt
|
||||
run: |
|
||||
make deps
|
||||
|
||||
- name: Run test
|
||||
working-directory: ./go/src/github.com/purpleidea/mgmt
|
||||
run: |
|
||||
TEST_BLOCK="${{ matrix.test_block }}" make test
|
||||
22
.gitignore
vendored
22
.gitignore
vendored
@@ -2,11 +2,25 @@
|
||||
.omv/
|
||||
.ssh/
|
||||
.vagrant/
|
||||
mgmt-documentation.pdf
|
||||
.envrc
|
||||
old/
|
||||
tmp/
|
||||
/vendor/
|
||||
*WIP
|
||||
*_stringer.go
|
||||
mgmt
|
||||
mgmt.static
|
||||
/mgmt
|
||||
/mgmt.static
|
||||
# crossbuild artifacts
|
||||
/build/mgmt-*
|
||||
mgmt.iml
|
||||
rpmbuild/
|
||||
/rpmbuild/
|
||||
/releases/
|
||||
/repository/
|
||||
/pprof/
|
||||
/sites/
|
||||
# vim swap files
|
||||
.*.sw[op]
|
||||
# prevent `echo foo 2>1` typo errors by making this file read-only
|
||||
1
|
||||
# allow users to keep some junk files around
|
||||
*.wip
|
||||
|
||||
15
.gitmodules
vendored
15
.gitmodules
vendored
@@ -1,15 +0,0 @@
|
||||
[submodule "vendor/github.com/coreos/etcd"]
|
||||
path = vendor/github.com/coreos/etcd
|
||||
url = https://github.com/coreos/etcd/
|
||||
[submodule "vendor/google.golang.org/grpc"]
|
||||
path = vendor/google.golang.org/grpc
|
||||
url = https://github.com/grpc/grpc-go
|
||||
[submodule "vendor/github.com/grpc-ecosystem/grpc-gateway"]
|
||||
path = vendor/github.com/grpc-ecosystem/grpc-gateway
|
||||
url = https://github.com/grpc-ecosystem/grpc-gateway
|
||||
[submodule "vendor/gopkg.in/fsnotify.v1"]
|
||||
path = vendor/gopkg.in/fsnotify.v1
|
||||
url = https://gopkg.in/fsnotify.v1
|
||||
[submodule "vendor/github.com/purpleidea/go-systemd"]
|
||||
path = vendor/github.com/purpleidea/go-systemd
|
||||
url = https://github.com/purpleidea/go-systemd
|
||||
3
.lycheeignore
Normal file
3
.lycheeignore
Normal file
@@ -0,0 +1,3 @@
|
||||
# list URLs that should be excluded for lychee link checher
|
||||
https://roidelapluie.be
|
||||
https://github.com/purpleidea/mgmt/commit
|
||||
28
.travis.yml
28
.travis.yml
@@ -1,28 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.6
|
||||
- 1.7
|
||||
- tip
|
||||
sudo: true
|
||||
dist: trusty
|
||||
before_install: 'git fetch --unshallow'
|
||||
install: 'make deps'
|
||||
script: 'make test'
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- go: tip
|
||||
- go: 1.7
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "irc.freenode.net#mgmtconfig"
|
||||
template:
|
||||
- "%{repository} (%{commit}: %{author}): %{message}"
|
||||
- "More info : %{build_url}"
|
||||
on_success: always
|
||||
on_failure: always
|
||||
use_notice: false
|
||||
skip_join: false
|
||||
email:
|
||||
- travis-ci@shubin.ca
|
||||
8
AUTHORS
8
AUTHORS
@@ -1,8 +1,14 @@
|
||||
This is a list of authors/contributors to the mgmt project.
|
||||
If you're a contributor, please send a patch with your name.
|
||||
If you're a core contributor, we might ask you to send a patch with your name.
|
||||
If you appreciate the work of one of the contributors, thank them a beverage!
|
||||
For a more exhaustive list please run: git log --format='%aN' | sort -u
|
||||
This list is sorted alphabetically by first name.
|
||||
|
||||
Felix Frank
|
||||
James Shubin
|
||||
Joe Groocock
|
||||
Johan Bloemberg
|
||||
Jonathan Gold
|
||||
Julien Pivotto
|
||||
Paul Morgan
|
||||
Samuel Gélineau
|
||||
|
||||
147
COPYING
147
COPYING
@@ -1,21 +1,23 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
@@ -24,34 +26,44 @@ them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
@@ -60,7 +72,7 @@ modification follow.
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
@@ -537,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
@@ -633,29 +635,40 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
22
COPYRIGHT
22
COPYRIGHT
@@ -1,16 +1,28 @@
|
||||
Mgmt
|
||||
Copyright (C) 2013-2016+ James Shubin and the project contributors
|
||||
Copyright (C) James Shubin and the project contributors
|
||||
Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permission under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this program, or any covered work, by linking or combining it
|
||||
with embedded mcl code and modules (and that the embedded mcl code and
|
||||
modules which link with this program, contain a copy of their source code in
|
||||
the authoritative form) containing parts covered by the terms of any other
|
||||
license, the licensors of this program grant you additional permission to
|
||||
convey the resulting work. Furthermore, the licensors of this program grant
|
||||
the original author, James Shubin, additional permission to update this
|
||||
additional permission if he deems it necessary to achieve the goals of this
|
||||
additional permission.
|
||||
|
||||
564
DOCUMENTATION.md
564
DOCUMENTATION.md
@@ -1,564 +0,0 @@
|
||||
#mgmt
|
||||
|
||||
<!--
|
||||
Mgmt
|
||||
Copyright (C) 2013-2016+ James Shubin and the project contributors
|
||||
Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
##mgmt by [James](https://ttboj.wordpress.com/)
|
||||
####Available from:
|
||||
####[https://github.com/purpleidea/mgmt/](https://github.com/purpleidea/mgmt/)
|
||||
|
||||
####This documentation is available in: [Markdown](https://github.com/purpleidea/mgmt/blob/master/DOCUMENTATION.md) or [PDF](https://pdfdoc-purpleidea.rhcloud.com/pdf/https://github.com/purpleidea/mgmt/blob/master/DOCUMENTATION.md) format.
|
||||
|
||||
####Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Project description - What the project does](#project-description)
|
||||
3. [Setup - Getting started with mgmt](#setup)
|
||||
4. [Features - All things mgmt can do](#features)
|
||||
* [Autoedges - Automatic resource relationships](#autoedges)
|
||||
* [Autogrouping - Automatic resource grouping](#autogrouping)
|
||||
* [Automatic clustering - Automatic cluster management](#automatic-clustering)
|
||||
* [Remote mode - Remote "agent-less" execution](#remote-agent-less-mode)
|
||||
* [Puppet support - write manifest code for mgmt](#puppet-support)
|
||||
5. [Resources - All built-in primitives](#resources)
|
||||
6. [Usage/FAQ - Notes on usage and frequently asked questions](#usage-and-frequently-asked-questions)
|
||||
7. [Reference - Detailed reference](#reference)
|
||||
* [Meta parameters](#meta-parameters)
|
||||
* [Graph definition file](#graph-definition-file)
|
||||
* [Command line](#command-line)
|
||||
8. [Examples - Example configurations](#examples)
|
||||
9. [Development - Background on module development and reporting bugs](#development)
|
||||
10. [Authors - Authors and contact information](#authors)
|
||||
|
||||
##Overview
|
||||
|
||||
The `mgmt` tool is a next generation config management prototype. It's not yet
|
||||
ready for production, but we hope to get there soon. Get involved today!
|
||||
|
||||
##Project Description
|
||||
|
||||
The mgmt tool is a distributed, event driven, config management tool, that
|
||||
supports parallel execution, and librarification to be used as the management
|
||||
foundation in and for, new and existing software.
|
||||
|
||||
For more information, you may like to read some blog posts from the author:
|
||||
|
||||
* [Next generation config mgmt](https://ttboj.wordpress.com/2016/01/18/next-generation-configuration-mgmt/)
|
||||
* [Automatic edges in mgmt](https://ttboj.wordpress.com/2016/03/14/automatic-edges-in-mgmt/)
|
||||
* [Automatic grouping in mgmt](https://ttboj.wordpress.com/2016/03/30/automatic-grouping-in-mgmt/)
|
||||
* [Automatic clustering in mgmt](https://ttboj.wordpress.com/2016/06/20/automatic-clustering-in-mgmt/)
|
||||
|
||||
There is also an [introductory video](http://meetings-archive.debian.net/pub/debian-meetings/2016/debconf16/Next_Generation_Config_Mgmt.webm) available.
|
||||
Older videos and other material [is available](https://github.com/purpleidea/mgmt/#on-the-web).
|
||||
|
||||
##Setup
|
||||
|
||||
During this prototype phase, the tool can be run out of the source directory.
|
||||
You'll probably want to use ```./run.sh run --yaml examples/graph1.yaml``` to
|
||||
get started. Beware that this _can_ cause data loss. Understand what you're
|
||||
doing first, or perform these actions in a virtual environment such as the one
|
||||
provided by [Oh-My-Vagrant](https://github.com/purpleidea/oh-my-vagrant).
|
||||
|
||||
##Features
|
||||
|
||||
This section details the numerous features of mgmt and some caveats you might
|
||||
need to be aware of.
|
||||
|
||||
###Autoedges
|
||||
|
||||
Automatic edges, or AutoEdges, is the mechanism in mgmt by which it will
|
||||
automatically create dependencies for you between resources. For example,
|
||||
since mgmt can discover which files are installed by a package it will
|
||||
automatically ensure that any file resource you declare that matches a
|
||||
file installed by your package resource will only be processed after the
|
||||
package is installed.
|
||||
|
||||
####Controlling autoedges
|
||||
|
||||
Though autoedges is likely to be very helpful and avoid you having to declare
|
||||
all dependencies explicitly, there are cases where this behaviour is
|
||||
undesirable.
|
||||
|
||||
Some distributions allow package installations to automatically start the
|
||||
service they ship. This can be problematic in the case of packages like MySQL
|
||||
as there are configuration options that need to be set before MySQL is ever
|
||||
started for the first time (or you'll need to wipe the data directory). In
|
||||
order to handle this situation you can disable autoedges per resource and
|
||||
explicitly declare that you want `my.cnf` to be written to disk before the
|
||||
installation of the `mysql-server` package.
|
||||
|
||||
You can disable autoedges for a resource by setting the `autoedge` key on
|
||||
the meta attributes of that resource to `false`.
|
||||
|
||||
####Blog post
|
||||
|
||||
You can read the introductory blog post about this topic here:
|
||||
[https://ttboj.wordpress.com/2016/03/14/automatic-edges-in-mgmt/](https://ttboj.wordpress.com/2016/03/14/automatic-edges-in-mgmt/)
|
||||
|
||||
###Autogrouping
|
||||
|
||||
Automatic grouping or AutoGroup is the mechanism in mgmt by which it will
|
||||
automatically group multiple resource vertices into a single one. This is
|
||||
particularly useful for grouping multiple package resources into a single
|
||||
resource, since the multiple installations can happen together in a single
|
||||
transaction, which saves a lot of time because package resources typically have
|
||||
a large fixed cost to running (downloading and verifying the package repo) and
|
||||
if they are grouped they share this fixed cost. This grouping feature can be
|
||||
used for other use cases too.
|
||||
|
||||
You can disable autogrouping for a resource by setting the `autogroup` key on
|
||||
the meta attributes of that resource to `false`.
|
||||
|
||||
####Blog post
|
||||
|
||||
You can read the introductory blog post about this topic here:
|
||||
[https://ttboj.wordpress.com/2016/03/30/automatic-grouping-in-mgmt/](https://ttboj.wordpress.com/2016/03/30/automatic-grouping-in-mgmt/)
|
||||
|
||||
###Automatic clustering
|
||||
|
||||
Automatic clustering is a feature by which mgmt automatically builds, scales,
|
||||
and manages the embedded etcd cluster which is compiled into mgmt itself. It is
|
||||
quite helpful for rapidly bootstrapping clusters and avoiding the extra work to
|
||||
setup etcd.
|
||||
|
||||
If you prefer to avoid this feature. you can always opt to use an existing etcd
|
||||
cluster that is managed separately from mgmt by pointing your mgmt agents at it
|
||||
with the `--seeds` variable.
|
||||
|
||||
####Blog post
|
||||
|
||||
You can read the introductory blog post about this topic here:
|
||||
[https://ttboj.wordpress.com/2016/06/20/automatic-clustering-in-mgmt/](https://ttboj.wordpress.com/2016/06/20/automatic-clustering-in-mgmt/)
|
||||
|
||||
###Remote ("agent-less") mode
|
||||
|
||||
Remote mode is a special mode that lets you kick off mgmt runs on one or more
|
||||
remote machines which are only accessible via SSH. In this mode the initiating
|
||||
host connects over SSH, copies over the `mgmt` binary, opens an SSH tunnel, and
|
||||
runs the remote program while simultaneously passing the etcd traffic back
|
||||
through the tunnel so that the initiators etcd cluster can be used to exchange
|
||||
resource data.
|
||||
|
||||
The interesting benefit of this architecture is that multiple hosts which can't
|
||||
connect directly use the initiator to pass the important traffic through to each
|
||||
other. Once the cluster has converged all the remote programs can shutdown
|
||||
leaving no residual agent.
|
||||
|
||||
This mode can also be useful for bootstrapping a new host where you'd like to
|
||||
have the service run continuously and as part of an mgmt cluster normally.
|
||||
|
||||
In particular, when combined with the `--converged-timeout` parameter, the
|
||||
entire set of running mgmt agents will need to all simultaneously converge for
|
||||
the group to exit. This is particularly useful for bootstrapping new clusters
|
||||
which need to exchange information that is only available at run time.
|
||||
|
||||
####Blog post
|
||||
|
||||
You can read the introductory blog post about this topic here:
|
||||
[https://ttboj.wordpress.com/2016/10/07/remote-execution-in-mgmt/](https://ttboj.wordpress.com/2016/10/07/remote-execution-in-mgmt/)
|
||||
|
||||
###Puppet support
|
||||
|
||||
You can supply a Puppet manifest instead of creating the (YAML) graph manually.
|
||||
Puppet must be installed and in `mgmt`'s search path. You also need the
|
||||
[ffrank-mgmtgraph Puppet module](https://forge.puppet.com/ffrank/mgmtgraph).
|
||||
|
||||
Invoke `mgmt` with the `--puppet` switch, which supports 3 variants:
|
||||
|
||||
1. Request the configuration from the Puppet Master (like `puppet agent` does)
|
||||
|
||||
mgmt run --puppet agent
|
||||
|
||||
2. Compile a local manifest file (like `puppet apply`)
|
||||
|
||||
mgmt run --puppet /path/to/my/manifest.pp
|
||||
|
||||
3. Compile an ad hoc manifest from the commandline (like `puppet apply -e`)
|
||||
|
||||
mgmt run --puppet 'file { "/etc/ntp.conf": ensure => file }'
|
||||
|
||||
For more details and caveats see [Puppet.md](Puppet.md).
|
||||
|
||||
####Blog post
|
||||
|
||||
An introductory post on the Puppet support is on
|
||||
[Felix's blog](http://ffrank.github.io/features/2016/06/19/puppet-powered-mgmt/).
|
||||
|
||||
##Resources
|
||||
|
||||
This section lists all the built-in resources and their properties. The
|
||||
resource primitives in `mgmt` are typically more powerful than resources in
|
||||
other configuration management systems because they can be event based which
|
||||
lets them respond in real-time to converge to the desired state. This property
|
||||
allows you to build more complex resources that you probably hadn't considered
|
||||
in the past.
|
||||
|
||||
In addition to the resource specific properties, there are resource properties
|
||||
(otherwise known as parameters) which can apply to every resource. These are
|
||||
called [meta parameters](#meta-parameters) and are listed separately. Certain
|
||||
meta parameters aren't very useful when combined with certain resources, but
|
||||
in general, it should be fairly obvious, such as when combining the `noop` meta
|
||||
parameter with the [Noop](#Noop) resource.
|
||||
|
||||
* [Exec](#Exec): Execute shell commands on the system.
|
||||
* [File](#File): Manage files and directories.
|
||||
* [Hostname](#Hostname): Manages the hostname on the system.
|
||||
* [Msg](#Msg): Send log messages.
|
||||
* [Noop](#Noop): A simple resource that does nothing.
|
||||
* [Nspawn](#Nspawn): Manage systemd-machined nspawn containers.
|
||||
* [Password](#Password): Create random password strings.
|
||||
* [Pkg](#Pkg): Manage system packages with PackageKit.
|
||||
* [Svc](#Svc): Manage system systemd services.
|
||||
* [Timer](#Timer): Manage system systemd services.
|
||||
* [Virt](#Virt): Manage virtual machines with libvirt.
|
||||
|
||||
###Exec
|
||||
|
||||
The exec resource can execute commands on your system.
|
||||
|
||||
###File
|
||||
|
||||
The file resource manages files and directories. In `mgmt`, directories are
|
||||
identified by a trailing slash in their path name. File have no such slash.
|
||||
|
||||
####Path
|
||||
|
||||
The path property specifies the file or directory that we are managing.
|
||||
|
||||
####Content
|
||||
|
||||
The content property is a string that specifies the desired file contents.
|
||||
|
||||
####Source
|
||||
|
||||
The source property points to a source file or directory path that we wish to
|
||||
copy over and use as the desired contents for our resource.
|
||||
|
||||
####State
|
||||
|
||||
The state property describes the action we'd like to apply for the resource. The
|
||||
possible values are: `exists` and `absent`.
|
||||
|
||||
####Recurse
|
||||
|
||||
The recurse property limits whether file resource operations should recurse into
|
||||
and monitor directory contents with a depth greater than one.
|
||||
|
||||
####Force
|
||||
|
||||
The force property is required if we want the file resource to be able to change
|
||||
a file into a directory or vice-versa. If such a change is needed, but the force
|
||||
property is not set to `true`, then this file resource will error.
|
||||
|
||||
###Hostname
|
||||
|
||||
The hostname resource manages static, transient/dynamic and pretty hostnames
|
||||
on the system and watches them for changes.
|
||||
|
||||
#### static_hostname
|
||||
The static hostname is the one configured in /etc/hostname or a similar
|
||||
file.
|
||||
It is chosen by the local user. It is not always in sync with the current
|
||||
host name as returned by the gethostname() system call.
|
||||
|
||||
#### transient_hostname
|
||||
The transient / dynamic hostname is the one configured via the kernel's
|
||||
sethostbyname().
|
||||
It can be different from the static hostname in case DHCP or mDNS have been
|
||||
configured to change the name based on network information.
|
||||
|
||||
#### pretty_hostname
|
||||
The pretty hostname is a free-form UTF8 host name for presentation to the user.
|
||||
|
||||
#### hostname
|
||||
Hostname is the fallback value for all 3 fields above, if only `hostname` is
|
||||
specified, it will set all 3 fields to this value.
|
||||
|
||||
###Msg
|
||||
|
||||
The msg resource sends messages to the main log, or an external service such
|
||||
as systemd's journal.
|
||||
|
||||
###Noop
|
||||
|
||||
The noop resource does absolutely nothing. It does have some utility in testing
|
||||
`mgmt` and also as a placeholder in the resource graph.
|
||||
|
||||
###Nspawn
|
||||
|
||||
The nspawn resource is used to manage systemd-machined style containers.
|
||||
|
||||
###Password
|
||||
|
||||
The password resource can generate a random string to be used as a password. It
|
||||
will re-generate the password if it receives a refresh notification.
|
||||
|
||||
###Pkg
|
||||
|
||||
The pkg resource is used to manage system packages. This resource works on many
|
||||
different distributions because it uses the underlying packagekit facility which
|
||||
supports different backends for different environments. This ensures that we
|
||||
have great Debian (deb/dpkg) and Fedora (rpm/dnf) support simultaneously.
|
||||
|
||||
###Svc
|
||||
|
||||
The service resource is still very WIP. Please help us my improving it!
|
||||
|
||||
###Timer
|
||||
|
||||
This resource needs better documentation. Please help us my improving it!
|
||||
|
||||
###Virt
|
||||
|
||||
The virt resource can manage virtual machines via libvirt.
|
||||
|
||||
##Usage and frequently asked questions
|
||||
(Send your questions as a patch to this FAQ! I'll review it, merge it, and
|
||||
respond by commit with the answer.)
|
||||
|
||||
###Why did you start this project?
|
||||
|
||||
I wanted a next generation config management solution that didn't have all of
|
||||
the design flaws or limitations that the current generation of tools do, and no
|
||||
tool existed!
|
||||
|
||||
###Why did you use etcd? What about consul?
|
||||
|
||||
Etcd and consul are both written in golang, which made them the top two
|
||||
contenders for my prototype. Ultimately a choice had to be made, and etcd was
|
||||
chosen, but it was also somewhat arbitrary. If there is available interest,
|
||||
good reasoning, *and* patches, then we would consider either switching or
|
||||
supporting both, but this is not a high priority at this time.
|
||||
|
||||
###Can I use an existing etcd cluster instead of the automatic embedded servers?
|
||||
|
||||
Yes, it's possible to use an existing etcd cluster instead of the automatic,
|
||||
elastic embedded etcd servers. To do so, simply point to the cluster with the
|
||||
`--seeds` variable, the same way you would if you were seeding a new member to
|
||||
an existing mgmt cluster.
|
||||
|
||||
The downside to this approach is that you won't benefit from the automatic
|
||||
elastic nature of the embedded etcd servers, and that you're responsible if you
|
||||
accidentally break your etcd cluster, or if you use an unsupported version.
|
||||
|
||||
###What does the error message about an inconsistent dataDir mean?
|
||||
|
||||
If you get an error message similar to:
|
||||
|
||||
```
|
||||
Etcd: Connect: CtxError...
|
||||
Etcd: CtxError: Reason: CtxDelayErr(5s): No endpoints available yet!
|
||||
Etcd: Connect: Endpoints: []
|
||||
Etcd: The dataDir (/var/lib/mgmt/etcd) might be inconsistent or corrupt.
|
||||
```
|
||||
|
||||
This happens when there are a series of fatal connect errors in a row. This can
|
||||
happen when you start `mgmt` using a dataDir that doesn't correspond to the
|
||||
current cluster view. As a result, the embedded etcd server never finishes
|
||||
starting up, and as a result, a default endpoint never gets added. The solution
|
||||
is to either reconcile the mistake, and if there is no important data saved, you
|
||||
can remove the etcd dataDir. This is typically `/var/lib/mgmt/etcd/member/`.
|
||||
|
||||
###Why do resources have both a `Compare` method and an `IFF` (on the UID) method?
|
||||
|
||||
The `Compare()` methods are for determining if two resources are effectively the
|
||||
same, which is used to make graph change delta's efficient. This is when we want
|
||||
to change from the current running graph to a new graph, but preserve the common
|
||||
vertices. Since we want to make this process efficient, we only update the parts
|
||||
that are different, and leave everything else alone. This `Compare()` method can
|
||||
tell us if two resources are the same.
|
||||
|
||||
The `IFF()` method is part of the whole UID system, which is for discerning if a
|
||||
resource meets the requirements another expects for an automatic edge. This is
|
||||
because the automatic edge system assumes a unified UID pattern to test for
|
||||
equality. In the future it might be helpful or sane to merge the two similar
|
||||
comparison functions although for now they are separate because they are
|
||||
actually answer different questions.
|
||||
|
||||
###Did you know that there is a band named `MGMT`?
|
||||
|
||||
I didn't realize this when naming the project, and it is accidental. After much
|
||||
anguishing, I chose the name because it was short and I thought it was
|
||||
appropriately descriptive. If you need a less ambiguous search term or phrase,
|
||||
you can try using `mgmtconfig` or `mgmt config`.
|
||||
|
||||
###You didn't answer my question, or I have a question!
|
||||
|
||||
It's best to ask on [IRC](https://webchat.freenode.net/?channels=#mgmtconfig)
|
||||
to see if someone can help you. Once we get a big enough community going, we'll
|
||||
add a mailing list. If you don't get any response from the above, you can
|
||||
contact me through my [technical blog](https://ttboj.wordpress.com/contact/)
|
||||
and I'll do my best to help. If you have a good question, please add it as a
|
||||
patch to this documentation. I'll merge your question, and add a patch with the
|
||||
answer!
|
||||
|
||||
##Reference
|
||||
Please note that there are a number of undocumented options. For more
|
||||
information on these options, please view the source at:
|
||||
[https://github.com/purpleidea/mgmt/](https://github.com/purpleidea/mgmt/).
|
||||
If you feel that a well used option needs documenting here, please patch it!
|
||||
|
||||
###Overview of reference
|
||||
* [Meta parameters](#meta-parameters): List of available resource meta parameters.
|
||||
* [Graph definition file](#graph-definition-file): Main graph definition file.
|
||||
* [Command line](#command-line): Command line parameters.
|
||||
|
||||
###Meta parameters
|
||||
These meta parameters are special parameters (or properties) which can apply to
|
||||
any resource. The usefulness of doing so will depend on the particular meta
|
||||
parameter and resource combination.
|
||||
|
||||
####AutoEdge
|
||||
Boolean. Should we generate auto edges for this resource?
|
||||
|
||||
####AutoGroup
|
||||
Boolean. Should we attempt to automatically group this resource with others?
|
||||
|
||||
####Noop
|
||||
Boolean. Should the Apply portion of the CheckApply method of the resource
|
||||
make any changes? Noop is a concatenation of no-operation.
|
||||
|
||||
####Retry
|
||||
Integer. The number of times to retry running the resource on error. Use -1 for
|
||||
infinite. This currently applies for both the Watch operation (which can fail)
|
||||
and for the CheckApply operation. While they could have separate values, I've
|
||||
decided to use the same ones for both until there's a proper reason to want to
|
||||
do something differently for the Watch errors.
|
||||
|
||||
####Delay
|
||||
Integer. Number of milliseconds to wait between retries. The same value is
|
||||
shared between the Watch and CheckApply retries. This currently applies for both
|
||||
the Watch operation (which can fail) and for the CheckApply operation. While
|
||||
they could have separate values, I've decided to use the same ones for both
|
||||
until there's a proper reason to want to do something differently for the Watch
|
||||
errors.
|
||||
|
||||
###Graph definition file
|
||||
graph.yaml is the compiled graph definition file. The format is currently
|
||||
undocumented, but by looking through the [examples/](https://github.com/purpleidea/mgmt/tree/master/examples)
|
||||
you can probably figure out most of it, as it's fairly intuitive.
|
||||
|
||||
###Command line
|
||||
The main interface to the `mgmt` tool is the command line. For the most recent
|
||||
documentation, please run `mgmt --help`.
|
||||
|
||||
####`--yaml <graph.yaml>`
|
||||
Point to a graph file to run.
|
||||
|
||||
####`--converged-timeout <seconds>`
|
||||
Exit if the machine has converged for approximately this many seconds.
|
||||
|
||||
####`--max-runtime <seconds>`
|
||||
Exit when the agent has run for approximately this many seconds. This is not
|
||||
generally recommended, but may be useful for users who know what they're doing.
|
||||
|
||||
####`--noop`
|
||||
Globally force all resources into no-op mode. This also disables the export to
|
||||
etcd functionality, but does not disable resource collection, however all
|
||||
resources that are collected will have their individual noop settings set.
|
||||
|
||||
####`--remote <graph.yaml>`
|
||||
Point to a graph file to run on the remote host specified within. This parameter
|
||||
can be used multiple times if you'd like to remotely run on multiple hosts in
|
||||
parallel.
|
||||
|
||||
####`--allow-interactive`
|
||||
Allow interactive prompting for SSH passwords if there is no authentication
|
||||
method that works.
|
||||
|
||||
####`--ssh-priv-id-rsa`
|
||||
Specify the path for finding SSH keys. This defaults to `~/.ssh/id_rsa`. To
|
||||
never use this method of authentication, set this to the empty string.
|
||||
|
||||
####`--cconns`
|
||||
The maximum number of concurrent remote ssh connections to run. This defaults
|
||||
to `0`, which means unlimited.
|
||||
|
||||
####`--no-caching`
|
||||
Don't allow remote caching of the remote execution binary. This will require
|
||||
the binary to be copied over for every remote execution, but it limits the
|
||||
likelihood that there is leftover information from the configuration process.
|
||||
|
||||
####`--prefix <path>`
|
||||
Specify a path to a custom working directory prefix. This directory will get
|
||||
created if it does not exist. This usually defaults to `/var/lib/mgmt/`. This
|
||||
can't be combined with the `--tmp-prefix` option. It can be combined with the
|
||||
`--allow-tmp-prefix` option.
|
||||
|
||||
####`--tmp-prefix`
|
||||
If this option is specified, a temporary prefix will be used instead of the
|
||||
default prefix. This can't be combined with the `--prefix` option.
|
||||
|
||||
####`--allow-tmp-prefix`
|
||||
If this option is specified, we will attempt to fall back to a temporary prefix
|
||||
if the primary prefix couldn't be created. This is useful for avoiding failures
|
||||
in environments where the primary prefix may or may not be available, but you'd
|
||||
like to try. The canonical example is when running `mgmt` with `--remote` there
|
||||
might be a cached copy of the binary in the primary prefix, but in case there's
|
||||
no binary available continue working in a temporary directory to avoid failure.
|
||||
|
||||
##Examples
|
||||
For example configurations, please consult the [examples/](https://github.com/purpleidea/mgmt/tree/master/examples) directory in the git
|
||||
source repository. It is available from:
|
||||
|
||||
[https://github.com/purpleidea/mgmt/tree/master/examples](https://github.com/purpleidea/mgmt/tree/master/examples)
|
||||
|
||||
### Systemd:
|
||||
See [`misc/mgmt.service`](misc/mgmt.service) for a sample systemd unit file.
|
||||
This unit file is part of the RPM.
|
||||
|
||||
To specify your custom options for `mgmt` on a systemd distro:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /etc/systemd/system/mgmt.service.d/
|
||||
|
||||
cat > /etc/systemd/system/mgmt.service.d/env.conf <<EOF
|
||||
# Environment variables:
|
||||
MGMT_SEEDS=http://127.0.0.1:2379
|
||||
MGMT_CONVERGED_TIMEOUT=-1
|
||||
MGMT_MAX_RUNTIME=0
|
||||
|
||||
# Other CLI options if necessary.
|
||||
#OPTS="--max-runtime=0"
|
||||
EOF
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
##Development
|
||||
|
||||
This is a project that I started in my free time in 2013. Development is driven
|
||||
by all of our collective patches! Dive right in, and start hacking!
|
||||
Please contact me if you'd like to invite me to speak about this at your event.
|
||||
|
||||
You can follow along [on my technical blog](https://ttboj.wordpress.com/).
|
||||
|
||||
To report any bugs, please file a ticket at: [https://github.com/purpleidea/mgmt/issues](https://github.com/purpleidea/mgmt/issues).
|
||||
|
||||
##Authors
|
||||
|
||||
Copyright (C) 2013-2016+ James Shubin and the project contributors
|
||||
|
||||
Please see the
|
||||
[AUTHORS](https://github.com/purpleidea/mgmt/tree/master/AUTHORS) file
|
||||
for more information.
|
||||
|
||||
* [github](https://github.com/purpleidea/)
|
||||
* [@purpleidea](https://twitter.com/#!/purpleidea)
|
||||
* [https://ttboj.wordpress.com/](https://ttboj.wordpress.com/)
|
||||
473
Makefile
473
Makefile
@@ -1,33 +1,61 @@
|
||||
# Mgmt
|
||||
# Copyright (C) 2013-2016+ James Shubin and the project contributors
|
||||
# Copyright (C) James Shubin and the project contributors
|
||||
# Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Additional permission under GNU GPL version 3 section 7
|
||||
#
|
||||
# If you modify this program, or any covered work, by linking or combining it
|
||||
# with embedded mcl code and modules (and that the embedded mcl code and
|
||||
# modules which link with this program, contain a copy of their source code in
|
||||
# the authoritative form) containing parts covered by the terms of any other
|
||||
# license, the licensors of this program grant you additional permission to
|
||||
# convey the resulting work. Furthermore, the licensors of this program grant
|
||||
# the original author, James Shubin, additional permission to update this
|
||||
# additional permission if he deems it necessary to achieve the goals of this
|
||||
# additional permission.
|
||||
|
||||
SHELL = /usr/bin/env bash
|
||||
.PHONY: all art cleanart version program path deps run race generate build clean test gofmt yamlfmt format docs rpmbuild mkdirs rpm srpm spec tar upload upload-sources upload-srpms upload-rpms copr
|
||||
SHELL = bash
|
||||
.PHONY: all art cleanart version program lang path deps run race generate build build-debug crossbuild clean test gofmt yamlfmt format docs
|
||||
.PHONY: rpmbuild mkdirs rpm srpm spec tar upload upload-sources upload-srpms upload-rpms upload-releases copr tag
|
||||
.PHONY: mkosi mkosi_fedora-latest mkosi_fedora-older mkosi_stream-latest mkosi_debian-stable mkosi_ubuntu-latest mkosi_archlinux
|
||||
.PHONY: release release_test releases_path release_binary_amd64 release_binary_arm64 release_fedora-latest release_fedora-older release_stream-latest release_debian-stable release_ubuntu-latest release_archlinux
|
||||
.PHONY: funcgen
|
||||
.SILENT: clean
|
||||
|
||||
# a large amount of output from this `find`, can cause `make` to be much slower!
|
||||
GO_FILES := $(shell find * -name '*.go' -not -path 'old/*' -not -path 'tmp/*')
|
||||
MCL_FILES := $(shell find lang/ -name '*.mcl' -not -path 'old/*' -not -path 'tmp/*')
|
||||
MISC_FILES := $(shell find engine/resources/http_server_ui/)
|
||||
|
||||
SVERSION := $(or $(SVERSION),$(shell git describe --match '[0-9]*\.[0-9]*\.[0-9]*' --tags --dirty --always))
|
||||
VERSION := $(or $(VERSION),$(shell git describe --match '[0-9]*\.[0-9]*\.[0-9]*' --tags --abbrev=0))
|
||||
PROGRAM := $(shell echo $(notdir $(CURDIR)) | cut -f1 -d"-")
|
||||
OLDGOLANG := $(shell go version | grep -E 'go1.3|go1.4')
|
||||
PKGNAME := $(shell go list .)
|
||||
ifeq ($(VERSION),$(SVERSION))
|
||||
RELEASE = 1
|
||||
else
|
||||
RELEASE = untagged
|
||||
endif
|
||||
# debugging is harder if -trimpath is set, so disable it if env var is set
|
||||
# this is force enabled automatically when using the release target
|
||||
ifeq ($(MGMT_NOTRIMPATH),true)
|
||||
TRIMPATH =
|
||||
else
|
||||
TRIMPATH = -trimpath
|
||||
endif
|
||||
ARCH = $(uname -m)
|
||||
SPEC = rpmbuild/SPECS/$(PROGRAM).spec
|
||||
SOURCE = rpmbuild/SOURCES/$(PROGRAM)-$(VERSION).tar.bz2
|
||||
@@ -36,12 +64,87 @@ SRPM_BASE = $(PROGRAM)-$(VERSION)-$(RELEASE).src.rpm
|
||||
RPM = rpmbuild/RPMS/$(PROGRAM)-$(VERSION)-$(RELEASE).$(ARCH).rpm
|
||||
USERNAME := $(shell cat ~/.config/copr 2>/dev/null | grep username | awk -F '=' '{print $$2}' | tr -d ' ')
|
||||
SERVER = 'dl.fedoraproject.org'
|
||||
REMOTE_PATH = 'pub/alt/$(USERNAME)/$(PROGRAM)'
|
||||
REMOTE_PATH = '/srv/pub/alt/$(USERNAME)/$(PROGRAM)'
|
||||
ifneq ($(GOTAGS),)
|
||||
BUILD_FLAGS = -tags '$(GOTAGS)'
|
||||
endif
|
||||
GOOSARCHES ?= linux/amd64 linux/ppc64 linux/ppc64le linux/arm64 darwin/amd64
|
||||
|
||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
||||
|
||||
# The underscores separate the prefix name ("TOKEN") the distro ("BINARY",
|
||||
# "FEDORA-LATEST", etc...) and the arch ("AMD64"). The distro can have a dash.
|
||||
TOKEN_BINARY_AMD64 = $(shell grep -v '#' releases/binary-amd64.release)
|
||||
TOKEN_BINARY_ARM64 = $(shell grep -v '#' releases/binary-arm64.release)
|
||||
TOKEN_FEDORA-LATEST = $(shell grep -v '#' releases/fedora-latest.release)
|
||||
TOKEN_FEDORA-OLDER = $(shell grep -v '#' releases/fedora-older.release)
|
||||
TOKEN_STREAM-LATEST = $(shell grep -v '#' releases/stream-latest.release)
|
||||
TOKEN_DEBIAN-STABLE = $(shell grep -v '#' releases/debian-stable.release)
|
||||
TOKEN_UBUNTU-LATEST = $(shell grep -v '#' releases/ubuntu-latest.release)
|
||||
TOKEN_ARCHLINUX = $(shell grep -v '#' releases/archlinux.release)
|
||||
|
||||
FILE_BINARY_AMD64 = mgmt-linux-amd64-$(VERSION)
|
||||
FILE_BINARY_ARM64 = mgmt-linux-arm64-$(VERSION)
|
||||
# TODO: add ARCH onto these at the end, eg _AMD64
|
||||
FILE_FEDORA-LATEST = mgmt-$(TOKEN_FEDORA-LATEST)-$(VERSION)-1.x86_64.rpm
|
||||
FILE_FEDORA-OLDER = mgmt-$(TOKEN_FEDORA-OLDER)-$(VERSION)-1.x86_64.rpm
|
||||
FILE_STREAM-LATEST = mgmt-$(TOKEN_STREAM-LATEST)-$(VERSION)-1.x86_64.rpm
|
||||
FILE_DEBIAN-STABLE = mgmt_$(TOKEN_DEBIAN-STABLE)_$(VERSION)_amd64.deb
|
||||
FILE_UBUNTU-LATEST = mgmt_$(TOKEN_UBUNTU-LATEST)_$(VERSION)_amd64.deb
|
||||
FILE_ARCHLINUX = mgmt-$(TOKEN_ARCHLINUX)-$(VERSION)-1-x86_64.pkg.tar.xz
|
||||
|
||||
PKG_BINARY_AMD64 = releases/$(VERSION)/$(TOKEN_BINARY_AMD64)/$(FILE_BINARY_AMD64)
|
||||
PKG_BINARY_ARM64 = releases/$(VERSION)/$(TOKEN_BINARY_ARM64)/$(FILE_BINARY_ARM64)
|
||||
PKG_FEDORA-LATEST = releases/$(VERSION)/$(TOKEN_FEDORA-LATEST)/$(FILE_FEDORA-LATEST)
|
||||
PKG_FEDORA-OLDER = releases/$(VERSION)/$(TOKEN_FEDORA-OLDER)/$(FILE_FEDORA-OLDER)
|
||||
PKG_STREAM-LATEST = releases/$(VERSION)/$(TOKEN_STREAM-LATEST)/$(FILE_STREAM-LATEST)
|
||||
PKG_DEBIAN-STABLE = releases/$(VERSION)/$(TOKEN_DEBIAN-STABLE)/$(FILE_DEBIAN-STABLE)
|
||||
PKG_UBUNTU-LATEST = releases/$(VERSION)/$(TOKEN_UBUNTU-LATEST)/$(FILE_UBUNTU-LATEST)
|
||||
PKG_ARCHLINUX = releases/$(VERSION)/$(TOKEN_ARCHLINUX)/$(FILE_ARCHLINUX)
|
||||
|
||||
DEP_BINARY_AMD64 =
|
||||
ifneq ($(TOKEN_BINARY_AMD64),)
|
||||
DEP_BINARY_AMD64 = $(PKG_BINARY_AMD64)
|
||||
endif
|
||||
DEP_BINARY_ARM64 =
|
||||
ifneq ($(TOKEN_BINARY_ARM64),)
|
||||
DEP_BINARY_ARM64 = $(PKG_BINARY_ARM64)
|
||||
endif
|
||||
DEP_FEDORA-LATEST =
|
||||
ifneq ($(TOKEN_FEDORA-LATEST),)
|
||||
DEP_FEDORA-LATEST = $(PKG_FEDORA-LATEST)
|
||||
endif
|
||||
DEP_FEDORA-OLDER =
|
||||
ifneq ($(TOKEN_FEDORA-OLDER),)
|
||||
DEP_FEDORA-OLDER = $(PKG_FEDORA-OLDER)
|
||||
endif
|
||||
DEP_STREAM-LATEST =
|
||||
ifneq ($(TOKEN_STREAM-LATEST),)
|
||||
DEP_STREAM-LATEST = $(PKG_STREAM-LATEST)
|
||||
endif
|
||||
DEP_DEBIAN-STABLE =
|
||||
ifneq ($(TOKEN_DEBIAN-STABLE),)
|
||||
DEP_DEBIAN-STABLE = $(PKG_DEBIAN-STABLE)
|
||||
endif
|
||||
DEP_UBUNTU-LATEST =
|
||||
ifneq ($(TOKEN_UBUNTU-LATEST),)
|
||||
DEP_UBUNTU-LATEST = $(PKG_UBUNTU-LATEST)
|
||||
endif
|
||||
DEP_ARCHLINUX =
|
||||
ifneq ($(TOKEN_ARCHLINUX),)
|
||||
DEP_ARCHLINUX = $(PKG_ARCHLINUX)
|
||||
endif
|
||||
|
||||
SHA256SUMS = releases/$(VERSION)/SHA256SUMS
|
||||
SHA256SUMS_ASC = $(SHA256SUMS).asc
|
||||
|
||||
default: build
|
||||
|
||||
#
|
||||
# art
|
||||
#
|
||||
art: art/mgmt_logo_default_symbol.png art/mgmt_logo_default_tall.png art/mgmt_logo_default_wide.png art/mgmt_logo_reversed_symbol.png art/mgmt_logo_reversed_tall.png art/mgmt_logo_reversed_wide.png art/mgmt_logo_white_symbol.png art/mgmt_logo_white_tall.png art/mgmt_logo_white_wide.png
|
||||
art: art/mgmt_logo_default_symbol.png art/mgmt_logo_default_tall.png art/mgmt_logo_default_wide.png art/mgmt_logo_reversed_symbol.png art/mgmt_logo_reversed_tall.png art/mgmt_logo_reversed_wide.png art/mgmt_logo_white_symbol.png art/mgmt_logo_white_tall.png art/mgmt_logo_white_wide.png ## generate artwork
|
||||
|
||||
cleanart:
|
||||
rm -f art/mgmt_logo_default_symbol.png art/mgmt_logo_default_tall.png art/mgmt_logo_default_wide.png art/mgmt_logo_reversed_symbol.png art/mgmt_logo_reversed_tall.png art/mgmt_logo_reversed_wide.png art/mgmt_logo_white_symbol.png art/mgmt_logo_white_tall.png art/mgmt_logo_white_wide.png
|
||||
@@ -77,69 +180,111 @@ art/mgmt_logo_white_wide.png: art/mgmt_logo_white_wide.svg
|
||||
all: docs $(PROGRAM).static
|
||||
|
||||
# show the current version
|
||||
version:
|
||||
version: ## show the current version
|
||||
@echo $(VERSION)
|
||||
|
||||
program:
|
||||
program: ## show the program name
|
||||
@echo $(PROGRAM)
|
||||
|
||||
path:
|
||||
path: ## create working paths
|
||||
./misc/make-path.sh
|
||||
|
||||
deps:
|
||||
deps: ## install system and golang dependencies
|
||||
./misc/make-deps.sh
|
||||
|
||||
run:
|
||||
find . -maxdepth 1 -type f -name '*.go' -not -name '*_test.go' | xargs go run -ldflags "-X main.program=$(PROGRAM) -X main.version=$(SVERSION)"
|
||||
|
||||
# include race flag
|
||||
race:
|
||||
find . -maxdepth 1 -type f -name '*.go' -not -name '*_test.go' | xargs go run -race -ldflags "-X main.program=$(PROGRAM) -X main.version=$(SVERSION)"
|
||||
|
||||
generate:
|
||||
go generate
|
||||
|
||||
build: $(PROGRAM)
|
||||
lang: ## generates the lexer/parser for the language frontend
|
||||
@# recursively run make in child dir named lang
|
||||
@$(MAKE) --quiet -C lang
|
||||
|
||||
$(PROGRAM): main.go
|
||||
@echo "Building: $(PROGRAM), version: $(SVERSION)..."
|
||||
ifneq ($(OLDGOLANG),)
|
||||
@# avoid equals sign in old golang versions eg in: -X foo=bar
|
||||
time go build -ldflags "-X main.program $(PROGRAM) -X main.version $(SVERSION)" -o $(PROGRAM);
|
||||
else
|
||||
time go build -ldflags "-X main.program=$(PROGRAM) -X main.version=$(SVERSION)" -o $(PROGRAM);
|
||||
endif
|
||||
resources: ## builds the resources dependencies required for the engine backend
|
||||
@# recursively run make in child dir named engine/resources
|
||||
@$(MAKE) --quiet -C engine/resources
|
||||
|
||||
$(PROGRAM).static: main.go
|
||||
# build a `mgmt` binary for current host os/arch
|
||||
$(PROGRAM): build/mgmt-${GOHOSTOS}-${GOHOSTARCH} ## build an mgmt binary for current host os/arch
|
||||
cp -a $< $@
|
||||
|
||||
$(PROGRAM).static: $(GO_FILES) $(MCL_FILES) $(MISC_FILES) go.mod go.sum
|
||||
@echo "Building: $(PROGRAM).static, version: $(SVERSION)..."
|
||||
go generate
|
||||
ifneq ($(OLDGOLANG),)
|
||||
@# avoid equals sign in old golang versions eg in: -X foo=bar
|
||||
go build -a -installsuffix cgo -tags netgo -ldflags '-extldflags "-static" -X main.program $(PROGRAM) -X main.version $(SVERSION)' -o $(PROGRAM).static;
|
||||
else
|
||||
go build -a -installsuffix cgo -tags netgo -ldflags '-extldflags "-static" -X main.program=$(PROGRAM) -X main.version=$(SVERSION)' -o $(PROGRAM).static;
|
||||
endif
|
||||
go build $(TRIMPATH) -a -installsuffix cgo -tags netgo -ldflags '-extldflags "-static" -X main.program=$(PROGRAM) -X main.version=$(SVERSION) -s -w' -o $(PROGRAM).static $(BUILD_FLAGS);
|
||||
|
||||
clean:
|
||||
build: LDFLAGS=-s -w ## build a fresh mgmt binary
|
||||
build: $(PROGRAM)
|
||||
|
||||
build-debug: LDFLAGS=
|
||||
build-debug: $(PROGRAM)
|
||||
|
||||
# if you're using the bad/dev branch, you might want this too!
|
||||
baddev: BUILD_FLAGS = -tags 'noaugeas novirt'
|
||||
baddev: $(PROGRAM)
|
||||
|
||||
# pattern rule target for (cross)building, mgmt-OS-ARCH will be expanded to the correct build
|
||||
# extract os and arch from target pattern
|
||||
GOOS=$(firstword $(subst -, ,$*))
|
||||
GOARCH=$(lastword $(subst -, ,$*))
|
||||
build/mgmt-%: $(GO_FILES) $(MCL_FILES) $(MISC_FILES) go.mod go.sum | lang resources funcgen
|
||||
@# If you need to run `go mod tidy` then this can trigger.
|
||||
@if [ "$(PKGNAME)" = "" ]; then echo "\$$(PKGNAME) is empty, test with: go list ."; exit 42; fi
|
||||
@echo "Building: $(PROGRAM), os/arch: $*, version: $(SVERSION)..."
|
||||
@# XXX: leave race detector on by default for now. For production
|
||||
@# builds, we can consider turning it off for performance improvements.
|
||||
@# XXX: ./mgmt run --tmp-prefix lang something_fast.mcl > /tmp/race 2>&1 # search for "WARNING: DATA RACE"
|
||||
time env GOOS=${GOOS} GOARCH=${GOARCH} go build $(TRIMPATH) -race -ldflags=$(PKGNAME)="-X main.program=$(PROGRAM) -X main.version=$(SVERSION) ${LDFLAGS}" -o $@ $(BUILD_FLAGS)
|
||||
|
||||
# create a list of binary file names to use as make targets
|
||||
# to use this you might want to run something like:
|
||||
# GOOSARCHES='linux/arm64' GOTAGS='noaugeas novirt' make crossbuild
|
||||
# and the output will end up in build/
|
||||
crossbuild_targets = $(addprefix build/mgmt-,$(subst /,-,${GOOSARCHES}))
|
||||
crossbuild: ${crossbuild_targets}
|
||||
|
||||
clean: ## clean things up
|
||||
$(MAKE) --quiet -C test clean
|
||||
$(MAKE) --quiet -C lang clean
|
||||
$(MAKE) --quiet -C engine/resources clean
|
||||
$(MAKE) --quiet -C misc/mkosi clean
|
||||
rm -f lang/core/generated_funcs.go || true
|
||||
rm -f lang/core/generated_funcs_test.go || true
|
||||
[ ! -e $(PROGRAM) ] || rm $(PROGRAM)
|
||||
rm -f *_stringer.go # generated by `go generate`
|
||||
rm -f *_mock.go # generated by `go generate`
|
||||
# crossbuild artifacts
|
||||
rm -f build/mgmt-*
|
||||
|
||||
test:
|
||||
test: build ## run tests
|
||||
@# recursively run make in child dir named test
|
||||
@$(MAKE) --quiet -C test
|
||||
./test.sh
|
||||
|
||||
# create all test targets for make tab completion (eg: make test-gofmt)
|
||||
test_suites=$(shell find test/ -maxdepth 1 -name test-* -exec basename {} .sh \;)
|
||||
# allow to run only one test suite at a time
|
||||
${test_suites}: test-%: build
|
||||
./test.sh $*
|
||||
|
||||
# targets to run individual shell tests (eg: make test-shell-load0)
|
||||
test_shell=$(shell find test/shell/ -maxdepth 1 -name "*.sh" -exec basename {} .sh \;)
|
||||
$(addprefix test-shell-,${test_shell}): test-shell-%: build
|
||||
./test/test-shell.sh "$*.sh"
|
||||
|
||||
gofmt:
|
||||
find . -maxdepth 3 -type f -name '*.go' -not -path './old/*' -not -path './tmp/*' -exec gofmt -w {} \;
|
||||
# TODO: remove gofmt once goimports has a -s option
|
||||
find . -maxdepth 9 -type f -name '*.go' -not -path './old/*' -not -path './tmp/*' -not -path './vendor/*' -exec gofmt -s -w {} \;
|
||||
find . -maxdepth 9 -type f -name '*.go' -not -path './old/*' -not -path './tmp/*' -not -path './vendor/*' -exec goimports -w {} \;
|
||||
|
||||
yamlfmt:
|
||||
find . -maxdepth 3 -type f -name '*.yaml' -not -path './old/*' -not -path './tmp/*' -not -path './omv.yaml' -exec ruby -e "require 'yaml'; x=YAML.load_file('{}').to_yaml.each_line.map(&:rstrip).join(10.chr)+10.chr; File.open('{}', 'w').write x" \;
|
||||
|
||||
format: gofmt yamlfmt
|
||||
format: gofmt yamlfmt ## format yaml and golang code
|
||||
|
||||
docs: $(PROGRAM)-documentation.pdf
|
||||
docs: $(PROGRAM)-documentation.pdf ## generate docs
|
||||
|
||||
$(PROGRAM)-documentation.pdf: DOCUMENTATION.md
|
||||
pandoc DOCUMENTATION.md -o '$(PROGRAM)-documentation.pdf'
|
||||
$(PROGRAM)-documentation.pdf: docs/documentation.md
|
||||
pandoc docs/documentation.md -o docs/'$(PROGRAM)-documentation.pdf'
|
||||
|
||||
#
|
||||
# build aliases
|
||||
@@ -161,7 +306,7 @@ rpmbuild/SOURCES/: tar
|
||||
rpmbuild/SRPMS/: srpm
|
||||
rpmbuild/RPMS/: rpm
|
||||
|
||||
upload: upload-sources upload-srpms upload-rpms
|
||||
upload: upload-sources upload-srpms upload-rpms ## upload sources
|
||||
# do nothing
|
||||
|
||||
#
|
||||
@@ -183,7 +328,7 @@ $(SRPM): $(SPEC) $(SOURCE)
|
||||
#
|
||||
$(SPEC): rpmbuild/ spec.in
|
||||
@echo Running templater...
|
||||
#cat spec.in > $(SPEC)
|
||||
cat spec.in > $(SPEC)
|
||||
sed -e s/__PROGRAM__/$(PROGRAM)/g -e s/__VERSION__/$(VERSION)/g -e s/__RELEASE__/$(RELEASE)/g < spec.in > $(SPEC)
|
||||
# append a changelog to the .spec file
|
||||
git log --format="* %cd %aN <%aE>%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $(SPEC)
|
||||
@@ -266,10 +411,244 @@ upload-rpms: rpmbuild/RPMS/ rpmbuild/RPMS/SHA256SUMS rpmbuild/RPMS/SHA256SUMS.as
|
||||
rsync -avz --prune-empty-dirs rpmbuild/RPMS/ $(SERVER):$(REMOTE_PATH)/RPMS/; \
|
||||
fi
|
||||
|
||||
upload-releases:
|
||||
echo Running releases/ upload...
|
||||
rsync -avz --exclude '.mkdir' --exclude 'mgmt-release.url' releases/ $(SERVER):$(REMOTE_PATH)/releases/
|
||||
|
||||
#
|
||||
# copr build
|
||||
#
|
||||
copr: upload-srpms
|
||||
copr: upload-srpms ## build in copr
|
||||
./misc/copr-build.py https://$(SERVER)/$(REMOTE_PATH)/SRPMS/$(SRPM_BASE)
|
||||
|
||||
#
|
||||
# tag
|
||||
#
|
||||
tag: ## tags a new release
|
||||
./misc/tag.sh
|
||||
|
||||
#
|
||||
# mkosi
|
||||
#
|
||||
mkosi: mkosi_fedora-latest mkosi_fedora-older mkosi_stream-latest mkosi_debian-stable mkosi_ubuntu-latest mkosi_archlinux ## builds distro packages via mkosi
|
||||
|
||||
mkosi_fedora-latest: releases/$(VERSION)/.mkdir
|
||||
@title='$@' ; echo "Generating: $${title#'mkosi_'} via mkosi..."
|
||||
@title='$@' ; distro=$${title#'mkosi_'} ; ./misc/mkosi/make.sh $${distro} `realpath "releases/$(VERSION)/"`
|
||||
|
||||
mkosi_fedora-older: releases/$(VERSION)/.mkdir
|
||||
@title='$@' ; echo "Generating: $${title#'mkosi_'} via mkosi..."
|
||||
@title='$@' ; distro=$${title#'mkosi_'} ; ./misc/mkosi/make.sh $${distro} `realpath "releases/$(VERSION)/"`
|
||||
|
||||
mkosi_stream-latest: releases/$(VERSION)/.mkdir
|
||||
@title='$@' ; echo "Generating: $${title#'mkosi_'} via mkosi..."
|
||||
@title='$@' ; distro=$${title#'mkosi_'} ; ./misc/mkosi/make.sh $${distro} `realpath "releases/$(VERSION)/"`
|
||||
|
||||
mkosi_debian-stable: releases/$(VERSION)/.mkdir
|
||||
@title='$@' ; echo "Generating: $${title#'mkosi_'} via mkosi..."
|
||||
@title='$@' ; distro=$${title#'mkosi_'} ; ./misc/mkosi/make.sh $${distro} `realpath "releases/$(VERSION)/"`
|
||||
|
||||
mkosi_ubuntu-latest: releases/$(VERSION)/.mkdir
|
||||
@title='$@' ; echo "Generating: $${title#'mkosi_'} via mkosi..."
|
||||
@title='$@' ; distro=$${title#'mkosi_'} ; ./misc/mkosi/make.sh $${distro} `realpath "releases/$(VERSION)/"`
|
||||
|
||||
mkosi_archlinux: releases/$(VERSION)/.mkdir
|
||||
@title='$@' ; echo "Generating: $${title#'mkosi_'} via mkosi..."
|
||||
@title='$@' ; distro=$${title#'mkosi_'} ; ./misc/mkosi/make.sh $${distro} `realpath "releases/$(VERSION)/"`
|
||||
|
||||
#
|
||||
# release
|
||||
#
|
||||
release: TRIMPATH = -trimpath
|
||||
release: releases/$(VERSION)/mgmt-release.url ## generates and uploads a release
|
||||
|
||||
releases_path:
|
||||
@#Don't put any other output or dependencies in here or they'll show!
|
||||
@echo "releases/$(VERSION)/"
|
||||
|
||||
release_test: $(DEP_BINARY_AMD64) $(DEP_BINARY_ARM64) $(DEP_FEDORA-LATEST) $(DEP_FEDORA-OLDER) $(DEP_STREAM-LATEST) $(DEP_DEBIAN-STABLE) $(DEP_UBUNTU-LATEST) $(DEP_ARCHLINUX) $(SHA256SUMS_ASC)
|
||||
@echo '$$< denotes ‘the first dependency of the current rule’.'
|
||||
@echo '> '"$<"
|
||||
@echo
|
||||
@echo '$$@ denotes ‘the target of the current rule’.'
|
||||
@echo '> '"$@"
|
||||
@echo
|
||||
@echo '$$^ denotes ‘the dependencies of the current rule’.'
|
||||
@echo '> '"$^"
|
||||
@echo
|
||||
@echo '$$* denotes ‘the stem with which the pattern of the current rule matched’.'
|
||||
@echo '> '"$*"
|
||||
@echo
|
||||
@echo "TOKEN_BINARY_AMD64: $(TOKEN_BINARY_AMD64)"
|
||||
@echo "DEP_BINARY_AMD64: $(DEP_BINARY_AMD64)"
|
||||
@echo
|
||||
@echo "TOKEN_BINARY_ARM64: $(TOKEN_BINARY_ARM64)"
|
||||
@echo "DEP_BINARY_ARM64: $(DEP_BINARY_ARM64)"
|
||||
@echo
|
||||
@echo "TOKEN_FEDORA-LATEST: $(TOKEN_FEDORA-LATEST)"
|
||||
@echo "DEP_FEDORA-LATEST: $(DEP_FEDORA-LATEST)"
|
||||
@echo
|
||||
@echo "TOKEN_FEDORA-OLDER: $(TOKEN_FEDORA-OLDER)"
|
||||
@echo "DEP_FEDORA-OLDER: $(DEP_FEDORA-OLDER)"
|
||||
@echo
|
||||
@echo "TOKEN_STREAM-LATEST: $(TOKEN_STREAM-LATEST)"
|
||||
@echo "DEP_STREAM-LATEST: $(DEP_STREAM-LATEST)"
|
||||
@echo
|
||||
@echo "TOKEN_DEBIAN-STABLE: $(TOKEN_DEBIAN-STABLE)"
|
||||
@echo "DEP_DEBIAN-STABLE: $(DEP_DEBIAN-STABLE)"
|
||||
@echo
|
||||
@echo "TOKEN_UBUNTU-LATEST: $(TOKEN_UBUNTU-LATEST)"
|
||||
@echo "DEP_UBUNTU-LATEST: $(DEP_UBUNTU-LATEST)"
|
||||
@echo
|
||||
@echo "TOKEN_ARCHLINUX: $(TOKEN_ARCHLINUX)"
|
||||
@echo "DEP_ARCHLINUX: $(DEP_ARCHLINUX)"
|
||||
|
||||
release_binary_amd64: $(PKG_BINARY_AMD64)
|
||||
release_binary_arm64: $(PKG_BINARY_ARM64)
|
||||
release_fedora-latest: $(PKG_FEDORA-LATEST)
|
||||
release_fedora-older: $(PKG_FEDORA-OLDER)
|
||||
release_stream-latest: $(PKG_STREAM-LATEST)
|
||||
release_debian-stable: $(PKG_DEBIAN-STABLE)
|
||||
release_ubuntu-latest: $(PKG_UBUNTU-LATEST)
|
||||
release_archlinux: $(PKG_ARCHLINUX)
|
||||
|
||||
releases/$(VERSION)/mgmt-release.url: $(DEP_BINARY_AMD64) $(DEP_BINARY_ARM64) $(DEP_FEDORA-LATEST) $(DEP_FEDORA-OLDER) $(DEP_STREAM-LATEST) $(DEP_DEBIAN-STABLE) $(DEP_UBUNTU-LATEST) $(DEP_ARCHLINUX) $(SHA256SUMS_ASC)
|
||||
@echo "Pushing git tag $(VERSION) to origin..."
|
||||
git push origin $(VERSION)
|
||||
@echo "Creating github release..."
|
||||
hub release create \
|
||||
-F <( echo -e "$(VERSION)\n";echo "Verify the signatures of all packages before you use them. The signing key can be downloaded from https://purpleidea.com/contact/#pgp-key to verify the release." ) \
|
||||
` [ -e $(PKG_BINARY_AMD64) ] && printf -- "-a $(PKG_BINARY_AMD64)" ` \
|
||||
` [ -e $(PKG_BINARY_ARM64) ] && printf -- "-a $(PKG_BINARY_ARM64)" ` \
|
||||
` [ -e $(PKG_FEDORA-LATEST) ] && printf -- "-a $(PKG_FEDORA-LATEST)" ` \
|
||||
` [ -e $(PKG_FEDORA-OLDER) ] && printf -- "-a $(PKG_FEDORA-OLDER)" ` \
|
||||
` [ -e $(PKG_STREAM-LATEST) ] && printf -- "-a $(PKG_STREAM-LATEST)" ` \
|
||||
` [ -e $(PKG_DEBIAN-STABLE) ] && printf -- "-a $(PKG_DEBIAN-STABLE)" ` \
|
||||
` [ -e $(PKG_UBUNTU-LATEST) ] && printf -- "-a $(PKG_UBUNTU-LATEST)" ` \
|
||||
` [ -e $(PKG_ARCHLINUX) ] && printf -- "-a $(PKG_ARCHLINUX)" ` \
|
||||
-a $(SHA256SUMS_ASC) \
|
||||
$(VERSION) \
|
||||
> releases/$(VERSION)/mgmt-release.url \
|
||||
&& cat releases/$(VERSION)/mgmt-release.url \
|
||||
|| rm -f releases/$(VERSION)/mgmt-release.url
|
||||
|
||||
releases/$(VERSION)/.mkdir:
|
||||
mkdir -p \
|
||||
` [ "$(TOKEN_BINARY_AMD64)" != "" ] && printf -- "releases/$(VERSION)/$(TOKEN_BINARY_AMD64)/" ` \
|
||||
` [ "$(TOKEN_BINARY_ARM64)" != "" ] && printf -- "releases/$(VERSION)/$(TOKEN_BINARY_ARM64)/" ` \
|
||||
` [ "$(TOKEN_FEDORA-LATEST)" != "" ] && printf -- "releases/$(VERSION)/$(TOKEN_FEDORA-LATEST)/" ` \
|
||||
` [ "$(TOKEN_FEDORA-OLDER)" != "" ] && printf -- "releases/$(VERSION)/$(TOKEN_FEDORA-OLDER)/" ` \
|
||||
` [ "$(TOKEN_STREAM-LATEST)" != "" ] && printf -- "releases/$(VERSION)/$(TOKEN_STREAM-LATEST)/" ` \
|
||||
` [ "$(TOKEN_DEBIAN-STABLE)" != "" ] && printf -- "releases/$(VERSION)/$(TOKEN_DEBIAN-STABLE)/" ` \
|
||||
` [ "$(TOKEN_UBUNTU-LATEST)" != "" ] && printf -- "releases/$(VERSION)/$(TOKEN_UBUNTU-LATEST)/" ` \
|
||||
` [ "$(TOKEN_ARCHLINUX)" != "" ] && printf -- "releases/$(VERSION)/$(TOKEN_ARCHLINUX)/" ` \
|
||||
&& touch releases/$(VERSION)/.mkdir
|
||||
|
||||
# These are defined conditionally, since if the token is empty, they warn!
|
||||
ifneq ($(TOKEN_BINARY_AMD64),)
|
||||
$(PKG_BINARY_AMD64): build/mgmt-linux-amd64 releases/$(VERSION)/.mkdir
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Building: $${distro} package..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; cp -a build/mgmt-linux-amd64 $(PKG_BINARY_AMD64)
|
||||
endif
|
||||
ifneq ($(TOKEN_BINARY_ARM64),)
|
||||
$(PKG_BINARY_ARM64): build/mgmt-linux-arm64 releases/$(VERSION)/.mkdir
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Building: $${distro} package..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; cp -a build/mgmt-linux-arm64 $(PKG_BINARY_ARM64)
|
||||
endif
|
||||
ifneq ($(TOKEN_FEDORA-LATEST),)
|
||||
releases/$(VERSION)/$(TOKEN_FEDORA-LATEST)/changelog: $(PROGRAM) releases/$(VERSION)/.mkdir
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Generating: $${distro} changelog..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/make-rpm-changelog.sh "$${distro}" $(VERSION)
|
||||
|
||||
$(PKG_FEDORA-LATEST): releases/$(VERSION)/$(TOKEN_FEDORA-LATEST)/changelog
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Building: $${distro} package..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/fpm-pack.sh $${distro} $(VERSION) "$(FILE_FEDORA-LATEST)" libvirt-devel augeas-devel
|
||||
endif
|
||||
ifneq ($(TOKEN_FEDORA-OLDER),)
|
||||
releases/$(VERSION)/$(TOKEN_FEDORA-OLDER)/changelog: $(PROGRAM) releases/$(VERSION)/.mkdir
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Generating: $${distro} changelog..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/make-rpm-changelog.sh "$${distro}" $(VERSION)
|
||||
|
||||
$(PKG_FEDORA-OLDER): releases/$(VERSION)/$(TOKEN_FEDORA-OLDER)/changelog
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Building: $${distro} package..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/fpm-pack.sh $${distro} $(VERSION) "$(FILE_FEDORA-OLDER)" libvirt-devel augeas-devel
|
||||
endif
|
||||
ifneq ($(TOKEN_STREAM-LATEST),)
|
||||
releases/$(VERSION)/$(TOKEN_STREAM-LATEST)/changelog: $(PROGRAM) releases/$(VERSION)/.mkdir
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Generating: $${distro} changelog..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/make-rpm-changelog.sh "$${distro}" $(VERSION)
|
||||
|
||||
$(PKG_STREAM-LATEST): releases/$(VERSION)/$(TOKEN_STREAM-LATEST)/changelog
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Building: $${distro} package..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/fpm-pack.sh $${distro} $(VERSION) "$(FILE_STREAM-LATEST)" libvirt-devel augeas-devel
|
||||
endif
|
||||
ifneq ($(TOKEN_DEBIAN-STABLE),)
|
||||
releases/$(VERSION)/$(TOKEN_DEBIAN-STABLE)/changelog: $(PROGRAM) releases/$(VERSION)/.mkdir
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Generating: $${distro} changelog..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/make-deb-changelog.sh "$${distro}" $(VERSION)
|
||||
|
||||
$(PKG_DEBIAN-STABLE): releases/$(VERSION)/$(TOKEN_DEBIAN-STABLE)/changelog
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Building: $${distro} package..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/fpm-pack.sh $${distro} $(VERSION) "$(FILE_DEBIAN-STABLE)" libvirt-dev libaugeas-dev
|
||||
endif
|
||||
ifneq ($(TOKEN_UBUNTU-LATEST),)
|
||||
releases/$(VERSION)/$(TOKEN_UBUNTU-LATEST)/changelog: $(PROGRAM) releases/$(VERSION)/.mkdir
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Generating: $${distro} changelog..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/make-deb-changelog.sh "$${distro}" $(VERSION)
|
||||
|
||||
$(PKG_UBUNTU-LATEST): releases/$(VERSION)/$(TOKEN_UBUNTU-LATEST)/changelog
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Building: $${distro} package..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/fpm-pack.sh $${distro} $(VERSION) "$(FILE_UBUNTU-LATEST)" libvirt-dev libaugeas-dev
|
||||
endif
|
||||
ifneq ($(TOKEN_ARCHLINUX),)
|
||||
$(PKG_ARCHLINUX): $(PROGRAM) releases/$(VERSION)/.mkdir
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; echo "Building: $${distro} package..."
|
||||
@title='$(@D)' ; distro=$${title#'releases/$(VERSION)/'} ; ./misc/fpm-pack.sh $${distro} $(VERSION) "$(FILE_ARCHLINUX)" libvirt augeas
|
||||
endif
|
||||
|
||||
$(SHA256SUMS): $(DEP_BINARY_AMD64) $(DEP_BINARY_ARM64) $(DEP_FEDORA-LATEST) $(DEP_FEDORA-OLDER) $(DEP_STREAM-LATEST) $(DEP_DEBIAN-STABLE) $(DEP_UBUNTU-LATEST) $(DEP_ARCHLINUX)
|
||||
@# remove the directory separator in the SHA256SUMS file
|
||||
@echo "Generating: sha256 sum..."
|
||||
sha256sum \
|
||||
` [ -e $(PKG_BINARY_AMD64) ] && printf -- "$(PKG_BINARY_AMD64)" ` \
|
||||
` [ -e $(PKG_BINARY_ARM64) ] && printf -- "$(PKG_BINARY_ARM64)" ` \
|
||||
` [ -e $(PKG_FEDORA-LATEST) ] && printf -- "$(PKG_FEDORA-LATEST)" ` \
|
||||
` [ -e $(PKG_FEDORA-OLDER) ] && printf -- "$(PKG_FEDORA-OLDER)" ` \
|
||||
` [ -e $(PKG_STREAM-LATEST) ] && printf -- "$(PKG_STREAM-LATEST)" ` \
|
||||
` [ -e $(PKG_DEBIAN-STABLE) ] && printf -- "$(PKG_DEBIAN-STABLE)" ` \
|
||||
` [ -e $(PKG_UBUNTU-LATEST) ] && printf -- "$(PKG_UBUNTU-LATEST)" ` \
|
||||
` [ -e $(PKG_ARCHLINUX) ] && printf -- "$(PKG_ARCHLINUX)" ` \
|
||||
| awk -F '/| ' '{print $$1" "$$6}' > $(SHA256SUMS)
|
||||
|
||||
$(SHA256SUMS_ASC): $(SHA256SUMS)
|
||||
@echo "Signing sha256 sum..."
|
||||
gpg2 --yes --clearsign $(SHA256SUMS)
|
||||
|
||||
build_container: ## builds the container
|
||||
docker build -t purpleidea/mgmt-build -f docker/Dockerfile.build .
|
||||
docker run -td --name mgmt-build purpleidea/mgmt-build
|
||||
docker cp mgmt-build:/root/gopath/src/github.com/purpleidea/mgmt/mgmt .
|
||||
docker build -t purpleidea/mgmt -f docker/Dockerfile.static .
|
||||
docker rm mgmt-build || true
|
||||
|
||||
clean_container: ## removes the container
|
||||
docker rmi purpleidea/mgmt-build
|
||||
docker rmi purpleidea/mgmt
|
||||
|
||||
help: ## show this help screen
|
||||
@echo 'Usage: make <OPTIONS> ... <TARGETS>'
|
||||
@echo ''
|
||||
@echo 'Available targets are:'
|
||||
@echo ''
|
||||
@grep -E '^[ a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
|
||||
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
@echo ''
|
||||
|
||||
funcgen: lang/core/generated_funcs.go
|
||||
|
||||
lang/core/generated_funcs.go: lang/funcs/funcgen/*.go lang/core/funcgen.yaml lang/funcs/funcgen/templates/generated_funcs.go.tpl
|
||||
@echo "Generating: funcs..."
|
||||
@go run `find lang/funcs/funcgen/ -maxdepth 1 -type f -name '*.go' -not -name '*_test.go'` -templates=lang/funcs/funcgen/templates/generated_funcs.go.tpl >/dev/null
|
||||
@gofmt -s -w $@
|
||||
|
||||
# vim: ts=8
|
||||
|
||||
163
Puppet.md
163
Puppet.md
@@ -1,163 +0,0 @@
|
||||
#mgmt Puppet support
|
||||
|
||||
1. [Prerequisites](#prerequisites)
|
||||
* [Testing the Puppet side](#testing-the-puppet-side)
|
||||
2. [Writing a suitable manifest](#writing-a-suitable-manifest)
|
||||
* [Unsupported attributes](#unsupported-attributes)
|
||||
* [Unsupported resources](#unsupported-resources)
|
||||
* [Avoiding common warnings](#avoiding-common-warnings)
|
||||
3. [Configuring Puppet](#configuring-puppet)
|
||||
4. [Caveats](#caveats)
|
||||
|
||||
`mgmt` can use Puppet as its source for the configuration graph.
|
||||
This document goes into detail on how this works, and lists
|
||||
some pitfalls and limitations.
|
||||
|
||||
For basic instructions on how to use the Puppet support, see
|
||||
the [main documentation](DOCUMENTATION.md#puppet-support).
|
||||
|
||||
##Prerequisites
|
||||
|
||||
You need Puppet installed in your system. It is not important how you
|
||||
get it. On the most common Linux distributions, you can use packages
|
||||
from the OS maintainer, or upstream Puppet repositories. An alternative
|
||||
that will also work on OSX is the `puppet` Ruby gem. It also has the
|
||||
advantage that you can install any desired version in your home directory
|
||||
or any other location.
|
||||
|
||||
Any release of Puppet's 3.x and 4.x series should be suitable for use with
|
||||
`mgmt`. Most importantly, make sure to install the `ffrank-mgmtgraph` Puppet
|
||||
module (referred to below as "the translator module").
|
||||
|
||||
puppet module install ffrank-mgmtgraph
|
||||
|
||||
Please note that the module is not required on your Puppet master (if you
|
||||
use a master/agent setup). It's needed on the machine that runs `mgmt`.
|
||||
You can install the module on the master anyway, so that it gets distributed
|
||||
to your agents through Puppet's `pluginsync` mechanism.
|
||||
|
||||
###Testing the Puppet side
|
||||
|
||||
The following command should run successfully and print a YAML hash on your
|
||||
terminal:
|
||||
|
||||
```puppet
|
||||
puppet mgmtgraph print --code 'file { "/tmp/mgmt-test": ensure => present }'
|
||||
```
|
||||
|
||||
You can use this CLI to test any manifests before handing them straight
|
||||
to `mgmt`.
|
||||
|
||||
##Writing a suitable manifest
|
||||
|
||||
###Unsupported attributes
|
||||
|
||||
`mgmt` inherited its resource module from Puppet, so by and large, it's quite
|
||||
possible to express `mgmt` graphs in terms of Puppet manifests. However,
|
||||
there isn't (and likely never will be) full feature parity between the
|
||||
respective resource types. In consequence, a manifest can have semantics that
|
||||
cannot be transferred to `mgmt`.
|
||||
|
||||
For example, at the time of writing this, the `file` type in `mgmt` had no
|
||||
notion of permissions (the file `mode`) yet. This lead to the following
|
||||
warning (among others that will be discussed below):
|
||||
|
||||
$ puppet mgmtgraph print --code 'file { "/tmp/foo": mode => "0600" }'
|
||||
Warning: cannot translate: File[/tmp/foo] { mode => "600" } (attribute is ignored)
|
||||
|
||||
This is a heads-up for the user, because the resulting `mgmt` graph will
|
||||
in fact not pass this information to the `/tmp/foo` file resource, and
|
||||
`mgmt` will ignore this file's permissions. Including such attributes in
|
||||
manifests that are written expressly for `mgmt` is not sensible and should
|
||||
be avoided.
|
||||
|
||||
###Unsupported resources
|
||||
|
||||
Puppet has a fairly large number of
|
||||
[built-in types](https://docs.puppet.com/puppet/latest/reference/type.html),
|
||||
and countless more are available through
|
||||
[modules](https://forge.puppet.com/). It's unlikely that all of them will
|
||||
eventually receive native counterparts in `mgmt`.
|
||||
|
||||
When encountering an unknown resource, the translator module will replace
|
||||
it with an `exec` resource in its output. This resource will run the equivalent
|
||||
of a `puppet resource` command to make Puppet apply the original resource
|
||||
itself. This has quite abysmal performance, because processing such a
|
||||
resource requires the forking of at least one Puppet process (two if it
|
||||
is found to be out of sync). This comes with considerable overhead.
|
||||
On most systems, starting up any Puppet command takes several seconds.
|
||||
Compared to the split second that the actual work usually takes,
|
||||
this overhead can amount to several orders of magnitude.
|
||||
|
||||
Avoid Puppet types that `mgmt` does not implement (yet).
|
||||
|
||||
###Avoiding common warnings
|
||||
|
||||
Many resource parameters in Puppet take default values. For the most part,
|
||||
the translator module just ignores them. However, there are cases in which
|
||||
Puppet will default to convenient behavior that `mgmt` cannot quite replicate.
|
||||
For example, translating a plain `file` resource will lead to a warning message:
|
||||
|
||||
$ puppet mgmtgraph print --code 'file { "/tmp/mgmt-test": }'
|
||||
Warning: File[/tmp/mgmt-test] uses the 'puppet' file bucket, which mgmt cannot do. There will be no backup copies!
|
||||
|
||||
The reason is that per default, Puppet assumes the following parameter value
|
||||
(among others)
|
||||
|
||||
```puppet
|
||||
file { "/tmp/mgmt-test":
|
||||
backup => 'puppet',
|
||||
}
|
||||
```
|
||||
|
||||
To avoid this, specify the parameter explicitly:
|
||||
|
||||
$ puppet mgmtgraph print --code 'file { "/tmp/mgmt-test": backup => false }'
|
||||
|
||||
This is tedious in a more complex manifest. A good simplification is the
|
||||
following [resource default](https://docs.puppet.com/puppet/latest/reference/lang_defaults.html)
|
||||
anywhere on the top scope of your manifest:
|
||||
|
||||
```puppet
|
||||
File { backup => false }
|
||||
```
|
||||
|
||||
If you encounter similar warnings from other types and/or parameters,
|
||||
use the same approach to silence them if possible.
|
||||
|
||||
##Configuring Puppet
|
||||
|
||||
Since `mgmt` uses an actual Puppet CLI behind the scenes, you might
|
||||
need to tweak some of Puppet's runtime options in order to make it
|
||||
do what you want. Reasons for this could be among the following:
|
||||
|
||||
* You use the `--puppet agent` variant and need to configure
|
||||
`servername`, `certname` and other master/agent-related options.
|
||||
* You don't want runtime information to end up in the `vardir`
|
||||
that is used by your regular `puppet agent`.
|
||||
* You install specific Puppet modules for `mgmt` in a non-standard
|
||||
location.
|
||||
|
||||
`mgmt` exposes only one Puppet option in order to allow you to
|
||||
control all of them, through its `--puppet-conf` option. It allows
|
||||
you to specify which `puppet.conf` file should be used during
|
||||
translation.
|
||||
|
||||
mgmt run --puppet /opt/my-manifest.pp --puppet-conf /etc/mgmt/puppet.conf
|
||||
|
||||
Within this file, you can just specify any needed options in the
|
||||
`[main]` section:
|
||||
|
||||
[main]
|
||||
server=mgmt-master.example.net
|
||||
vardir=/var/lib/mgmt/puppet
|
||||
|
||||
##Caveats
|
||||
|
||||
Please see the [README](https://github.com/ffrank/puppet-mgmtgraph/blob/master/README.md)
|
||||
of the translator module for the current state of supported and unsupported
|
||||
language features.
|
||||
|
||||
You should probably make sure to always use the latest release of
|
||||
both `ffrank-mgmtgraph` and `ffrank-yamlresource` (the latter is
|
||||
getting pulled in as a dependency of the former).
|
||||
216
README.md
216
README.md
@@ -2,112 +2,152 @@
|
||||
|
||||
[](art/)
|
||||
|
||||
[](https://goreportcard.com/report/github.com/purpleidea/mgmt)
|
||||
[](http://travis-ci.org/purpleidea/mgmt)
|
||||
[](DOCUMENTATION.md)
|
||||
[](https://godoc.org/github.com/purpleidea/mgmt)
|
||||
[](https://webchat.freenode.net/?channels=#mgmtconfig)
|
||||
[](https://ci.centos.org/job/purpleidea-mgmt/)
|
||||
[](https://copr.fedoraproject.org/coprs/purpleidea/mgmt/)
|
||||
[](https://aur.archlinux.org/packages/mgmt/)
|
||||
[](https://goreportcard.com/report/github.com/purpleidea/mgmt)
|
||||
[](https://github.com/purpleidea/mgmt/actions/)
|
||||
[](https://godocs.io/github.com/purpleidea/mgmt)
|
||||
[](https://matrix.to/#/#mgmtconfig:matrix.org)
|
||||
[](https://www.patreon.com/purpleidea)
|
||||
[](https://liberapay.com/purpleidea/donate)
|
||||
|
||||
> [!TIP]
|
||||
> [Resource reference guide now available!](https://mgmtconfig.com/docs/resources/)
|
||||
|
||||
> [!TIP]
|
||||
> [Function reference guide now available!](https://mgmtconfig.com/docs/functions/)
|
||||
|
||||
## About:
|
||||
|
||||
`Mgmt` is a real-time automation tool. It is familiar to existing configuration
|
||||
management software, but is drastically more powerful as it can allow you to
|
||||
build real-time, closed-loop feedback systems, in a very safe way, and with a
|
||||
surprisingly small amount of our `mcl` code. For example, the following code
|
||||
will ensure that your file server is set to read-only when it's friday.
|
||||
|
||||
```mcl
|
||||
import "datetime"
|
||||
$is_friday = datetime.weekday(datetime.now()) == "friday"
|
||||
file "/srv/files/" {
|
||||
state => $const.res.file.state.exists,
|
||||
mode => if $is_friday { # this updates the mode, the instant it changes!
|
||||
"0550"
|
||||
} else {
|
||||
"0770"
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
It can run continuously, intermittently, or on-demand, and in the first case, it
|
||||
will guarantee that your system is always in the desired state for that instant!
|
||||
In this mode it can run as a decentralized cluster of agents across your
|
||||
network, each exchanging information with the others in real-time, to respond to
|
||||
your changing needs. For example, if you want to ensure that some resource runs
|
||||
on a maximum of two hosts in your cluster, you can specify that as well:
|
||||
|
||||
```mcl
|
||||
import "sys"
|
||||
import "world"
|
||||
|
||||
# we'll set a few scheduling options:
|
||||
$opts = struct{strategy => "rr", max => 2, ttl => 10,}
|
||||
|
||||
# schedule in a particular namespace with options:
|
||||
$set = world.schedule("xsched", $opts)
|
||||
|
||||
if sys.hostname() in $set {
|
||||
# use your imagination to put something more complex right here...
|
||||
print "i got scheduled" {} # this will run on the chosen machines
|
||||
}
|
||||
```
|
||||
|
||||
As you add and remove hosts from the cluster, the real-time `schedule` function
|
||||
will dynamically pick up to two hosts from the available pool. These specific
|
||||
functions aren't intrinsic to the core design, and new ones can be easily added.
|
||||
|
||||
Please read on if you'd like to learn more...
|
||||
|
||||
## Community:
|
||||
Come join us on IRC in [#mgmtconfig](https://webchat.freenode.net/?channels=#mgmtconfig) on Freenode!
|
||||
You may like the [#mgmtconfig](https://twitter.com/hashtag/mgmtconfig) hashtag if you're on [Twitter](https://twitter.com/#!/purpleidea).
|
||||
|
||||
Come join us in the `mgmt` community!
|
||||
|
||||
| Medium | Link |
|
||||
|---|---|
|
||||
| Matrix | [#mgmtconfig](https://matrix.to/#/#mgmtconfig:matrix.org) on Matrix.org |
|
||||
| Twitter | [@mgmtconfig](https://twitter.com/mgmtconfig) & [#mgmtconfig](https://twitter.com/hashtag/mgmtconfig) |
|
||||
| Mailing list | [looking for a new home, suggestions welcome](https://gitlab.freedesktop.org/freedesktop/freedesktop/-/issues/1082) |
|
||||
| Patreon | [purpleidea](https://www.patreon.com/purpleidea) on Patreon |
|
||||
|
||||
## Status:
|
||||
Mgmt is a fairly new project.
|
||||
We're working towards being minimally useful for production environments.
|
||||
We aren't feature complete for what we'd consider a 1.x release yet.
|
||||
With your help you'll be able to influence our design and get us there sooner!
|
||||
|
||||
## Questions:
|
||||
Please join the [#mgmtconfig](https://webchat.freenode.net/?channels=#mgmtconfig) IRC community!
|
||||
If you have a well phrased question that might benefit others, consider asking it by sending a patch to the documentation [FAQ](https://github.com/purpleidea/mgmt/blob/master/DOCUMENTATION.md#usage-and-frequently-asked-questions) section. I'll merge your question, and a patch with the answer!
|
||||
Mgmt is a next generation automation tool. It has similarities to other tools in
|
||||
the configuration management space, but has a fast, modern, distributed systems
|
||||
approach. The project contains an engine and a language.
|
||||
[Please have a look at an introductory video or blog post.](docs/on-the-web.md)
|
||||
|
||||
## Quick start:
|
||||
* Make sure you have golang version 1.6 or greater installed.
|
||||
* If you do not have a GOPATH yet, create one and export it:
|
||||
```
|
||||
mkdir $HOME/gopath
|
||||
export GOPATH=$HOME/gopath
|
||||
```
|
||||
* You might also want to add the GOPATH to your `~/.bashrc` or `~/.profile`.
|
||||
* For more information you can read the [GOPATH documentation](https://golang.org/cmd/go/#hdr-GOPATH_environment_variable).
|
||||
* Next download the mgmt code base, and switch to that directory:
|
||||
```
|
||||
go get -u github.com/purpleidea/mgmt
|
||||
cd $GOPATH/src/github.com/purpleidea/mgmt
|
||||
```
|
||||
* Get the remaining golang deps with `go get ./...`, or run `make deps` if you're comfortable with how we install them.
|
||||
* Run `make build` to get a freshly built `mgmt` binary.
|
||||
* Run `time ./mgmt run --yaml examples/graph0.yaml --converged-timeout=5 --tmp-prefix` to try out a very simple example!
|
||||
* To run continuously in the default mode of operation, omit the `--converged-timeout` option.
|
||||
* Have fun hacking on our future technology!
|
||||
Mgmt is over ten years old! It is very powerful today, and has a solid
|
||||
foundation and architecture which has been polished over the years. As with all
|
||||
software, there are bugs to fix and improvements to be made, but I expect
|
||||
they're easy to hack through and fix if you find any. Interested users should
|
||||
start with the [official website](https://mgmtconfig.com/docs/).
|
||||
|
||||
## Examples:
|
||||
Please look in the [examples/](examples/) folder for more examples!
|
||||
## Sponsors:
|
||||
|
||||
Mgmt is generously sponsored by:
|
||||
|
||||
[](https://m9rx.com/)
|
||||
|
||||
Please reach out if you'd like to sponsor!
|
||||
|
||||
## Documentation:
|
||||
Please see: the manually created [DOCUMENTATION.md](DOCUMENTATION.md) (also available as [PDF](https://pdfdoc-purpleidea.rhcloud.com/pdf/https://github.com/purpleidea/mgmt/blob/master/DOCUMENTATION.md)) and the automatically generated [GoDoc documentation](https://godoc.org/github.com/purpleidea/mgmt).
|
||||
|
||||
## Roadmap:
|
||||
Please see: [TODO.md](TODO.md) for a list of upcoming work and TODO items.
|
||||
Please get involved by working on one of these items or by suggesting something else!
|
||||
Feel free to grab one of the straightforward [#mgmtlove](https://github.com/purpleidea/mgmt/labels/mgmtlove) issues if you're a first time contributor to the project or if you're unsure about what to hack on!
|
||||
Please read, enjoy and help improve our documentation!
|
||||
|
||||
| Documentation | Additional Notes |
|
||||
|---|---|
|
||||
| [quick start guide](docs/quick-start-guide.md) | for everyone |
|
||||
| [frequently asked questions](docs/faq.md) | for everyone |
|
||||
| [general documentation](docs/documentation.md) | for everyone |
|
||||
| [resource reference](https://mgmtconfig.com/docs/resources/) | for everyone |
|
||||
| [function reference](https://mgmtconfig.com/docs/functions/) | for everyone |
|
||||
| [language guide](docs/language-guide.md) | for everyone |
|
||||
| [function guide](docs/function-guide.md) | for mgmt developers |
|
||||
| [resource guide](docs/resource-guide.md) | for mgmt developers |
|
||||
| [style guide](docs/style-guide.md) | for mgmt developers |
|
||||
| [contributing guide](docs/contributing.md) | for mgmt contributors |
|
||||
| [service API guide](docs/service-guide.md) | for external developers |
|
||||
| [godoc API reference](https://godoc.org/github.com/purpleidea/mgmt) | for mgmt developers |
|
||||
| [prometheus guide](docs/prometheus.md) | for everyone |
|
||||
| [puppet guide](docs/puppet-guide.md) | for puppet sysadmins |
|
||||
| [development](docs/development.md) | for mgmt developers |
|
||||
| [videos](docs/on-the-web.md) | for everyone |
|
||||
| [blogs](docs/on-the-web.md) | for everyone |
|
||||
|
||||
## Questions:
|
||||
|
||||
Please ask in the [community](#community)!
|
||||
If you have a well phrased question that might benefit others, consider asking
|
||||
it by sending a patch to the [FAQ](docs/faq.md) section. I'll merge your
|
||||
question, and a patch with the answer!
|
||||
|
||||
## Get involved:
|
||||
|
||||
Feel free to grab one of the straightforward [#mgmtlove](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
issues if you're a first time contributor to the project or if you're unsure
|
||||
about what to hack on! Please get involved by working on one of these items or
|
||||
by suggesting something else! There are some lower priority issues and harder
|
||||
issues available in our [TODO](TODO.md) file. Please have a look.
|
||||
|
||||
## Bugs:
|
||||
Please set the `DEBUG` constant in [main.go](https://github.com/purpleidea/mgmt/blob/master/main.go) to `true`, and post the logs when you report the [issue](https://github.com/purpleidea/mgmt/issues).
|
||||
Bonus points if you provide a [shell](https://github.com/purpleidea/mgmt/tree/master/test/shell) or [OMV](https://github.com/purpleidea/mgmt/tree/master/test/omv) reproducible test case.
|
||||
Feel free to read my article on [debugging golang programs](https://ttboj.wordpress.com/2016/02/15/debugging-golang-programs/).
|
||||
|
||||
## Dependencies:
|
||||
* golang 1.6 or higher (required, available in most distros)
|
||||
* golang libraries (required, available with `go get`)
|
||||
```
|
||||
go get github.com/coreos/etcd/client
|
||||
go get gopkg.in/yaml.v2
|
||||
go get gopkg.in/fsnotify.v1
|
||||
go get github.com/urfave/cli
|
||||
go get github.com/coreos/go-systemd/dbus
|
||||
go get github.com/coreos/go-systemd/util
|
||||
go get github.com/coreos/pkg/capnslog
|
||||
go get github.com/rgbkrk/libvirt-go
|
||||
```
|
||||
* stringer (optional for building), available as a package on some platforms, otherwise via `go get`
|
||||
```
|
||||
go get golang.org/x/tools/cmd/stringer
|
||||
```
|
||||
* pandoc (optional, for building a pdf of the documentation)
|
||||
* graphviz (optional, for building a visual representation of the graph)
|
||||
Please set the `DEBUG` constant in [main.go](https://github.com/purpleidea/mgmt/blob/master/main.go)
|
||||
to `true`, and post the logs when you report the [issue](https://github.com/purpleidea/mgmt/issues).
|
||||
Feel free to read my article on [debugging golang programs](https://purpleidea.com/blog/2016/02/15/debugging-golang-programs/).
|
||||
|
||||
## Patches:
|
||||
|
||||
We'd love to have your patches! Please send them by email, or as a pull request.
|
||||
|
||||
## On the web:
|
||||
* James Shubin; blog: [Next generation configuration mgmt](https://ttboj.wordpress.com/2016/01/18/next-generation-configuration-mgmt/)
|
||||
* James Shubin; video: [Introductory recording from DevConf.cz 2016](https://www.youtube.com/watch?v=GVhpPF0j-iE&html5=1)
|
||||
* James Shubin; video: [Introductory recording from CfgMgmtCamp.eu 2016](https://www.youtube.com/watch?v=fNeooSiIRnA&html5=1)
|
||||
* Julian Dunn; video: [On mgmt at CfgMgmtCamp.eu 2016](https://www.youtube.com/watch?v=kfF9IATUask&t=1949&html5=1)
|
||||
* Walter Heck; slides: [On mgmt at CfgMgmtCamp.eu 2016](http://www.slideshare.net/olindata/configuration-management-time-for-a-4th-generation/3)
|
||||
* Marco Marongiu; blog: [On mgmt](http://syslog.me/2016/02/15/leap-or-die/)
|
||||
* Felix Frank; blog: [From Catalog To Mgmt (on puppet to mgmt "transpiling")](https://ffrank.github.io/features/2016/02/18/from-catalog-to-mgmt/)
|
||||
* James Shubin; blog: [Automatic edges in mgmt (...and the pkg resource)](https://ttboj.wordpress.com/2016/03/14/automatic-edges-in-mgmt/)
|
||||
* James Shubin; blog: [Automatic grouping in mgmt](https://ttboj.wordpress.com/2016/03/30/automatic-grouping-in-mgmt/)
|
||||
* John Arundel; tweet: [“Puppet’s days are numbered.”](https://twitter.com/bitfield/status/732157519142002688)
|
||||
* Felix Frank; blog: [Puppet, Meet Mgmt (on puppet to mgmt internals)](https://ffrank.github.io/features/2016/06/12/puppet,-meet-mgmt/)
|
||||
* Felix Frank; blog: [Puppet Powered Mgmt (puppet to mgmt tl;dr)](https://ffrank.github.io/features/2016/06/19/puppet-powered-mgmt/)
|
||||
* James Shubin; blog: [Automatic clustering in mgmt](https://ttboj.wordpress.com/2016/06/20/automatic-clustering-in-mgmt/)
|
||||
* James Shubin; video: [Recording from CoreOSFest 2016](https://www.youtube.com/watch?v=KVmDCUA42wc&html5=1)
|
||||
* James Shubin; video: [Recording from DebConf16](http://meetings-archive.debian.net/pub/debian-meetings/2016/debconf16/Next_Generation_Config_Mgmt.webm) ([Slides](https://annex.debconf.org//debconf-share/debconf16/slides/15-next-generation-config-mgmt.pdf))
|
||||
* Felix Frank; blog: [Edging It All In (puppet and mgmt edges)](https://ffrank.github.io/features/2016/07/12/edging-it-all-in/)
|
||||
* Felix Frank; blog: [Translating All The Things (puppet to mgmt translation warnings)](https://ffrank.github.io/features/2016/08/19/translating-all-the-things/)
|
||||
* James Shubin; video: [Recording from systemd.conf 2016](https://www.youtube.com/watch?v=jB992Zb3nH0&html5=1)
|
||||
* James Shubin; blog: [Remote execution in mgmt](https://ttboj.wordpress.com/2016/10/07/remote-execution-in-mgmt/)
|
||||
* James Shubin; video: [Recording from High Load Strategy 2016](https://vimeo.com/191493409)
|
||||
* James Shubin; video: [Recording from NLUUG 2016](https://www.youtube.com/watch?v=MmpwOQAb_SE&html5=1)
|
||||
* James Shubin; blog: [Send/Recv in mgmt](https://ttboj.wordpress.com/2016/12/07/sendrecv-in-mgmt/)
|
||||
|
||||
##
|
||||
[Blog posts and recorded talks about mgmt are listed here!](docs/on-the-web.md)
|
||||
|
||||
Happy hacking!
|
||||
|
||||
85
TODO.md
85
TODO.md
@@ -1,67 +1,90 @@
|
||||
# TODO
|
||||
If you're looking for something to do, look here!
|
||||
Let us know if you're working on one of the items.
|
||||
|
||||
Here is a TODO list of longstanding items that are either lower-priority, or
|
||||
more involved in terms of time, skill-level, and/or motivation.
|
||||
|
||||
Please have a look, and let us know if you're working on one of the items. It's
|
||||
best to open an issue to track your progress and to discuss any implementation
|
||||
questions you might have.
|
||||
|
||||
Lastly, if you'd like something different to work on, please ping @purpleidea
|
||||
and I'll create an issue tailored especially for your approximate golang skill
|
||||
level and available time commitment in terms of hours you'd need to spend on the
|
||||
patch.
|
||||
|
||||
Happy Hacking!
|
||||
|
||||
## Package resource
|
||||
|
||||
- [ ] getfiles support on debian [bug](https://github.com/hughsie/PackageKit/issues/118)
|
||||
- [ ] directory info on fedora [bug](https://github.com/hughsie/PackageKit/issues/117)
|
||||
- [ ] dnf blocker [bug](https://github.com/hughsie/PackageKit/issues/110)
|
||||
- [ ] install signal blocker [bug](https://github.com/hughsie/PackageKit/issues/109)
|
||||
|
||||
## File resource [bug](https://github.com/purpleidea/mgmt/issues/13) [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
- [ ] chown/chmod support [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
- [ ] user/group support [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
## File resource [bug](https://github.com/purpleidea/mgmt/issues/64) [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
|
||||
- [ ] recurse limit support [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
- [ ] fanotify support [bug](https://github.com/go-fsnotify/fsnotify/issues/114)
|
||||
|
||||
## Svc resource
|
||||
- [ ] base resource improvements
|
||||
|
||||
- [ ] refreshonly support [:heart:](https://github.com/purpleidea/mgmt/issues/464)
|
||||
|
||||
## Exec resource
|
||||
|
||||
- [ ] base resource improvements
|
||||
|
||||
## Timer resource
|
||||
- [ ] reset on recompile
|
||||
|
||||
- [ ] increment algorithm (linear, exponential, etc...) [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
|
||||
## User/Group resource
|
||||
- [ ] base resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
|
||||
- [ ] automatic edges to file resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
|
||||
## Virt (libvirt) resource
|
||||
- [ ] base resource [bug](https://github.com/purpleidea/mgmt/issues/25)
|
||||
|
||||
## Net (systemd-networkd) resource
|
||||
- [ ] base resource
|
||||
|
||||
## Nspawn (systemd-nspawn) resource
|
||||
- [ ] base resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
|
||||
## Mount (systemd-mount) resource
|
||||
- [ ] base resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
|
||||
## Cron (systemd-timer) resource
|
||||
- [ ] base resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
|
||||
## Http resource
|
||||
- [ ] base resource
|
||||
|
||||
- [ ] base resource [:heart:](https://github.com/purpleidea/mgmt/labels/mgmtlove)
|
||||
|
||||
## Etcd improvements
|
||||
- [ ] fix embedded etcd master race
|
||||
|
||||
- [ ] fix etcd race bug that only happens during CI testing (intermittently
|
||||
failing test case issue)
|
||||
|
||||
## Torrent/dht file transfer
|
||||
|
||||
- [ ] base plumbing
|
||||
|
||||
## GPG/Auth improvements
|
||||
|
||||
- [ ] base plumbing
|
||||
|
||||
## Resource improvements
|
||||
|
||||
- [ ] more reversible resources implemented
|
||||
- [ ] more "cloud" resources
|
||||
|
||||
## Language improvements
|
||||
- [ ] language design
|
||||
- [ ] lexer/parser
|
||||
|
||||
- [ ] more core functions
|
||||
- [ ] automatic language formatter, ala `gofmt`
|
||||
- [ ] gedit/gnome-builder/gtksourceview syntax highlighting
|
||||
- [ ] vim syntax highlighting
|
||||
- [ ] emacs syntax highlighting
|
||||
- [ ] emacs syntax highlighting: see `misc/emacs/` (needs updating)
|
||||
- [ ] exposed $error variable for feedback in the language
|
||||
- [ ] improve the printf function to add %[]s, %[]f ([]str, []float) and map,
|
||||
struct, nested etc... %v would be nice too!
|
||||
- [ ] add line/col/file annotations to AST so we can get locations of errors
|
||||
that the parser finds
|
||||
- [ ] add more error messages with the `%error` pattern in parser.y
|
||||
- [ ] we should have helper functions or language sugar to pull a field out of a
|
||||
struct, or a value out of a map, or an index out of a list, etc...
|
||||
|
||||
## Engine improvements
|
||||
|
||||
- [ ] add a "waiting for func" message in the func engine to notify the user
|
||||
about slow functions...
|
||||
|
||||
## Other
|
||||
- [ ] better error/retry handling
|
||||
- [ ] deb package target in Makefile
|
||||
|
||||
- [ ] reproducible builds
|
||||
- [ ] add your suggestions!
|
||||
|
||||
51
Vagrantfile
vendored
Normal file
51
Vagrantfile
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
Vagrant.configure(2) do |config|
|
||||
config.ssh.forward_agent = true
|
||||
config.ssh.username = 'vagrant'
|
||||
config.vm.network "private_network", ip: "192.168.219.2"
|
||||
|
||||
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||
|
||||
config.vm.define "mgmt-dev" do |instance|
|
||||
instance.vm.box = "bento/fedora-31"
|
||||
end
|
||||
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
v.memory = 1536
|
||||
v.cpus = 2
|
||||
end
|
||||
config.vm.provider "libvirt" do |v|
|
||||
v.memory = 2048
|
||||
end
|
||||
|
||||
config.vm.provision "file", source: "vagrant/motd", destination: ".motd"
|
||||
config.vm.provision "shell", inline: "cp ~vagrant/.motd /etc/motd"
|
||||
|
||||
config.vm.provision "file", source: "vagrant/mgmt.bashrc", destination: ".mgmt.bashrc"
|
||||
config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig"
|
||||
|
||||
config.vm.provision "shell", inline: "dnf install -y golang git make"
|
||||
|
||||
# set up packagekit
|
||||
config.vm.provision "shell" do |shell|
|
||||
shell.inline = <<-SCRIPT
|
||||
dnf install -y PackageKit
|
||||
systemctl enable packagekit
|
||||
systemctl start packagekit
|
||||
SCRIPT
|
||||
end
|
||||
|
||||
# set up vagrant home
|
||||
script = <<-SCRIPT
|
||||
grep -q 'mgmt\.bashrc' ~/.bashrc || echo '. ~/.mgmt.bashrc' >>~/.bashrc
|
||||
. ~/.mgmt.bashrc
|
||||
mkdir -p ~/gopath/src/github.com/purpleidea
|
||||
cd ~/gopath/src/github.com/purpleidea
|
||||
git clone https://github.com/purpleidea/mgmt --recursive
|
||||
cd mgmt
|
||||
make deps
|
||||
SCRIPT
|
||||
config.vm.provision "shell" do |shell|
|
||||
shell.privileged = false
|
||||
shell.inline = script
|
||||
end
|
||||
end
|
||||
BIN
art/m9rx.png
Normal file
BIN
art/m9rx.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
BIN
art/mgmt.png
BIN
art/mgmt.png
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 683 KiB |
BIN
art/mgmt_poohbear_meme.jpg
Normal file
BIN
art/mgmt_poohbear_meme.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
189
cli/cli.go
Normal file
189
cli/cli.go
Normal file
@@ -0,0 +1,189 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
// Package cli handles all of the core command line parsing. It's the first
|
||||
// entry point after the real main function, and it imports and runs our core
|
||||
// "lib".
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
cliUtil "github.com/purpleidea/mgmt/cli/util"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
|
||||
"github.com/alexflint/go-arg"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if _, err := arg.NewParser(arg.Config{}, &Args{}); err != nil { // sanity check
|
||||
panic(errwrap.Wrapf(err, "invalid args cli struct"))
|
||||
}
|
||||
}
|
||||
|
||||
// CLI is the entry point for using mgmt normally from the CLI.
|
||||
func CLI(ctx context.Context, data *cliUtil.Data) error {
|
||||
// test for sanity
|
||||
if data == nil {
|
||||
return fmt.Errorf("this CLI was not run correctly")
|
||||
}
|
||||
if data.Program == "" || data.Version == "" {
|
||||
return fmt.Errorf("program was not compiled correctly")
|
||||
}
|
||||
if data.Copying == "" {
|
||||
return fmt.Errorf("program copyrights were removed, can't run")
|
||||
}
|
||||
|
||||
args := Args{}
|
||||
args.version = data.Version // copy this in
|
||||
args.description = data.Tagline
|
||||
|
||||
config := arg.Config{
|
||||
Program: data.Program,
|
||||
}
|
||||
parser, err := arg.NewParser(config, &args)
|
||||
if err != nil {
|
||||
// programming error
|
||||
return errwrap.Wrapf(err, "cli config error")
|
||||
}
|
||||
err = parser.Parse(data.Args[1:]) // XXX: args[0] needs to be dropped
|
||||
if err == arg.ErrHelp {
|
||||
parser.WriteHelp(os.Stdout)
|
||||
return nil
|
||||
}
|
||||
if err == arg.ErrVersion {
|
||||
fmt.Printf("%s\n", data.Version) // byon: bring your own newline
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
//parser.WriteHelp(os.Stdout) // TODO: is doing this helpful?
|
||||
return cliUtil.CliParseError(err) // consistent errors
|
||||
}
|
||||
|
||||
// display the license
|
||||
if args.License {
|
||||
fmt.Printf("%s", data.Copying) // file comes with a trailing nl
|
||||
return nil
|
||||
}
|
||||
|
||||
if ok, err := args.Run(ctx, data); err != nil {
|
||||
return err
|
||||
} else if ok { // did we activate one of the commands?
|
||||
return nil
|
||||
}
|
||||
|
||||
// print help if no subcommands are set
|
||||
parser.WriteHelp(os.Stdout)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Args is the CLI parsing structure and type of the parsed result. This
|
||||
// particular struct is the top-most one.
|
||||
type Args struct {
|
||||
// XXX: We cannot have both subcommands and a positional argument.
|
||||
// XXX: I think it's a bug of this library that it can't handle argv[0].
|
||||
//Argv0 string `arg:"positional"`
|
||||
|
||||
License bool `arg:"--license" help:"display the license and exit"`
|
||||
|
||||
RunCmd *RunArgs `arg:"subcommand:run" help:"run code on this machine"`
|
||||
|
||||
DeployCmd *DeployArgs `arg:"subcommand:deploy" help:"deploy code into a cluster"`
|
||||
|
||||
SetupCmd *SetupArgs `arg:"subcommand:setup" help:"setup some bootstrapping tasks"`
|
||||
|
||||
FirstbootCmd *FirstbootArgs `arg:"subcommand:firstboot" help:"run some tasks on first boot"`
|
||||
|
||||
DocsCmd *DocsGenerateArgs `arg:"subcommand:docs" help:"generate documentation"`
|
||||
|
||||
ToolsCmd *ToolsArgs `arg:"subcommand:tools" help:"collection of useful tools"`
|
||||
|
||||
// This never runs, it gets preempted in the real main() function.
|
||||
// XXX: Can we do it nicely with the new arg parser? can it ignore all args?
|
||||
EtcdCmd *EtcdArgs `arg:"subcommand:etcd" help:"run standalone etcd"`
|
||||
|
||||
// version is a private handle for our version string.
|
||||
version string `arg:"-"` // ignored from parsing
|
||||
|
||||
// description is a private handle for our description string.
|
||||
description string `arg:"-"` // ignored from parsing
|
||||
}
|
||||
|
||||
// Version returns the version string. Implementing this signature is part of
|
||||
// the API for the cli library.
|
||||
func (obj *Args) Version() string {
|
||||
return obj.version
|
||||
}
|
||||
|
||||
// Description returns a description string. Implementing this signature is part
|
||||
// of the API for the cli library.
|
||||
func (obj *Args) Description() string {
|
||||
return obj.description
|
||||
}
|
||||
|
||||
// Run executes the correct subcommand. It errors if there's ever an error. It
|
||||
// returns true if we did activate one of the subcommands. It returns false if
|
||||
// we did not. This information is used so that the top-level parser can return
|
||||
// usage or help information if no subcommand activates.
|
||||
func (obj *Args) Run(ctx context.Context, data *cliUtil.Data) (bool, error) {
|
||||
if cmd := obj.RunCmd; cmd != nil {
|
||||
return cmd.Run(ctx, data)
|
||||
}
|
||||
|
||||
if cmd := obj.DeployCmd; cmd != nil {
|
||||
return cmd.Run(ctx, data)
|
||||
}
|
||||
|
||||
if cmd := obj.SetupCmd; cmd != nil {
|
||||
return cmd.Run(ctx, data)
|
||||
}
|
||||
|
||||
if cmd := obj.FirstbootCmd; cmd != nil {
|
||||
return cmd.Run(ctx, data)
|
||||
}
|
||||
|
||||
if cmd := obj.DocsCmd; cmd != nil {
|
||||
return cmd.Run(ctx, data)
|
||||
}
|
||||
|
||||
if cmd := obj.ToolsCmd; cmd != nil {
|
||||
return cmd.Run(ctx, data)
|
||||
}
|
||||
|
||||
// NOTE: we could return true, fmt.Errorf("...") if more than one did
|
||||
return false, nil // nobody activated
|
||||
}
|
||||
|
||||
// EtcdArgs is the CLI parsing structure and type of the parsed result. This
|
||||
// particular one is empty because the `etcd` subcommand is preempted in the
|
||||
// real main() function.
|
||||
type EtcdArgs struct{}
|
||||
314
cli/deploy.go
Normal file
314
cli/deploy.go
Normal file
@@ -0,0 +1,314 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
cliUtil "github.com/purpleidea/mgmt/cli/util"
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
"github.com/purpleidea/mgmt/etcd"
|
||||
"github.com/purpleidea/mgmt/etcd/client"
|
||||
etcdfs "github.com/purpleidea/mgmt/etcd/fs"
|
||||
etcdSSH "github.com/purpleidea/mgmt/etcd/ssh"
|
||||
"github.com/purpleidea/mgmt/gapi"
|
||||
"github.com/purpleidea/mgmt/lib"
|
||||
"github.com/purpleidea/mgmt/util"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
|
||||
git "github.com/go-git/go-git/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// DeployArgs is the CLI parsing structure and type of the parsed result. This
|
||||
// particular one contains all the common flags for the `deploy` subcommand
|
||||
// which all frontends can use.
|
||||
type DeployArgs struct {
|
||||
// SSHURL can be specified if we want to transport the SSH client
|
||||
// connection over SSH. If this is specified, the second hop is made
|
||||
// with the Seeds values, but they connect from this destination. You
|
||||
// can specify this in the standard james@server:22 format. This will
|
||||
// use your ~/.ssh/ directory for public key authentication and
|
||||
// verifying the host key in the known_hosts file. This must already be
|
||||
// setup for things to work.
|
||||
SSHURL string `arg:"--ssh-url" help:"transport the etcd client connection over SSH to this server"`
|
||||
|
||||
// SSHHostKey is the key part (which is already base64 encoded) from a
|
||||
// known_hosts file, representing the host we're connecting to. If this
|
||||
// is specified, then it overrides looking for it in the URL.
|
||||
SSHHostKey string `arg:"--ssh-hostkey" help:"use this ssh known hosts key when connecting over SSH"`
|
||||
|
||||
Seeds []string `arg:"--seeds,separate,env:MGMT_SEEDS" help:"default etcd client endpoints"`
|
||||
Noop bool `arg:"--noop" help:"globally force all resources into no-op mode"`
|
||||
Sema int `arg:"--sema" default:"-1" help:"globally add a semaphore to all resources with this lock count"`
|
||||
NoGit bool `arg:"--no-git" help:"don't look at git commit id for safe deploys"`
|
||||
Force bool `arg:"--force" help:"force a new deploy, even if the safety chain would break"`
|
||||
|
||||
NoAutoEdges bool `arg:"--no-autoedges" help:"skip the autoedges stage"`
|
||||
|
||||
DeployEmpty *cliUtil.EmptyArgs `arg:"subcommand:empty" help:"deploy empty payload"`
|
||||
DeployLang *cliUtil.LangArgs `arg:"subcommand:lang" help:"deploy lang (mcl) payload"`
|
||||
DeployYaml *cliUtil.YamlArgs `arg:"subcommand:yaml" help:"deploy yaml graph payload"`
|
||||
DeployPuppet *cliUtil.PuppetArgs `arg:"subcommand:puppet" help:"deploy puppet graph payload"`
|
||||
DeployLangPuppet *cliUtil.LangPuppetArgs `arg:"subcommand:langpuppet" help:"deploy langpuppet graph payload"`
|
||||
}
|
||||
|
||||
// Run executes the correct subcommand. It errors if there's ever an error. It
|
||||
// returns true if we did activate one of the subcommands. It returns false if
|
||||
// we did not. This information is used so that the top-level parser can return
|
||||
// usage or help information if no subcommand activates. This particular Run is
|
||||
// the run for the main `deploy` subcommand. This always requires a frontend to
|
||||
// deploy to the cluster, but if you don't want a graph, you can use the `empty`
|
||||
// frontend. The engine backend is agnostic to which frontend is deployed, in
|
||||
// fact, you can deploy with multiple different frontends, one after another, on
|
||||
// the same engine.
|
||||
func (obj *DeployArgs) Run(ctx context.Context, data *cliUtil.Data) (bool, error) {
|
||||
var name string
|
||||
var args interface{}
|
||||
if cmd := obj.DeployEmpty; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "empty"
|
||||
args = cmd
|
||||
}
|
||||
if cmd := obj.DeployLang; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "lang"
|
||||
args = cmd
|
||||
}
|
||||
if cmd := obj.DeployYaml; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "yaml"
|
||||
args = cmd
|
||||
}
|
||||
if cmd := obj.DeployPuppet; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "puppet"
|
||||
args = cmd
|
||||
}
|
||||
if cmd := obj.DeployLangPuppet; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "langpuppet"
|
||||
args = cmd
|
||||
}
|
||||
|
||||
// XXX: workaround https://github.com/alexflint/go-arg/issues/239
|
||||
gapiNames := gapi.Names() // list of registered names
|
||||
if l := len(obj.Seeds); name == "" && l > 1 {
|
||||
elem := obj.Seeds[l-2] // second to last element
|
||||
if util.StrInList(elem, gapiNames) {
|
||||
return false, cliUtil.CliParseError(cliUtil.MissingEquals) // consistent errors
|
||||
}
|
||||
}
|
||||
|
||||
fn, exists := gapi.RegisteredGAPIs[name]
|
||||
if !exists {
|
||||
return false, nil // did not activate
|
||||
}
|
||||
gapiObj := fn()
|
||||
|
||||
program, version := data.Program, data.Version
|
||||
Logf := func(format string, v ...interface{}) {
|
||||
data.Flags.Logf("deploy: "+format, v...)
|
||||
}
|
||||
|
||||
// TODO: consider adding a timeout based on an args.Timeout flag ?
|
||||
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
|
||||
defer cancel()
|
||||
|
||||
cliUtil.Hello(program, version, data.Flags) // say hello!
|
||||
defer Logf("goodbye!")
|
||||
|
||||
var hash, pHash string
|
||||
if !obj.NoGit {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return false, errwrap.Wrapf(err, "could not get current working directory")
|
||||
}
|
||||
repo, err := git.PlainOpen(wd)
|
||||
if err != nil {
|
||||
return false, errwrap.Wrapf(err, "could not open git repo")
|
||||
}
|
||||
|
||||
head, err := repo.Head()
|
||||
if err != nil {
|
||||
return false, errwrap.Wrapf(err, "could not read git HEAD")
|
||||
}
|
||||
|
||||
hash = head.Hash().String() // current commit id
|
||||
Logf("hash: %s", hash)
|
||||
|
||||
lo := &git.LogOptions{
|
||||
From: head.Hash(),
|
||||
}
|
||||
commits, err := repo.Log(lo)
|
||||
if err != nil {
|
||||
return false, errwrap.Wrapf(err, "could not read git log")
|
||||
}
|
||||
if _, err := commits.Next(); err != nil { // skip over HEAD
|
||||
return false, errwrap.Wrapf(err, "could not read HEAD in git log") // weird!
|
||||
}
|
||||
commit, err := commits.Next()
|
||||
if err == nil { // errors are okay, we might be empty
|
||||
pHash = commit.Hash.String() // previous commit id
|
||||
}
|
||||
Logf("previous deploy hash: %s", pHash)
|
||||
if obj.Force {
|
||||
pHash = "" // don't check this :(
|
||||
}
|
||||
if hash == "" {
|
||||
return false, errwrap.Wrapf(err, "could not get git deploy hash")
|
||||
}
|
||||
}
|
||||
|
||||
uniqueid := uuid.New() // panic's if it can't generate one :P
|
||||
|
||||
etcdClient := client.NewClientFromSeedsNamespace(
|
||||
obj.Seeds, // endpoints
|
||||
lib.NS,
|
||||
)
|
||||
if err := etcdClient.Init(); err != nil {
|
||||
return false, errwrap.Wrapf(err, "client Init failed")
|
||||
}
|
||||
defer func() {
|
||||
err := errwrap.Wrapf(etcdClient.Close(), "client Close failed")
|
||||
if err != nil {
|
||||
// TODO: cause the final exit code to be non-zero
|
||||
Logf("client cleanup error: %+v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var world engine.World
|
||||
world = &etcd.World{ // XXX: What should some of these fields be?
|
||||
Client: etcdClient, // XXX: remove me when etcdfs below is done
|
||||
Seeds: obj.Seeds,
|
||||
NS: lib.NS,
|
||||
//MetadataPrefix: lib.MetadataPrefix,
|
||||
//StoragePrefix: lib.StoragePrefix,
|
||||
//StandaloneFs: ???.DeployFs, // used for static deploys
|
||||
//GetURI: func() string {
|
||||
//},
|
||||
}
|
||||
if obj.SSHURL != "" { // alternate world implementation over SSH
|
||||
world = &etcdSSH.World{
|
||||
URL: obj.SSHURL,
|
||||
HostKey: obj.SSHHostKey,
|
||||
Seeds: obj.Seeds,
|
||||
NS: lib.NS,
|
||||
//MetadataPrefix: lib.MetadataPrefix,
|
||||
//StoragePrefix: lib.StoragePrefix,
|
||||
//StandaloneFs: ???.DeployFs, // used for static deploys
|
||||
//GetURI: func() string {
|
||||
//},
|
||||
}
|
||||
// XXX: We need to first get rid of the standalone etcd client,
|
||||
// and then pull the etcdfs stuff in so it uses that client.
|
||||
return false, fmt.Errorf("--ssh-url is not implemented yet")
|
||||
}
|
||||
worldInit := &engine.WorldInit{
|
||||
Hostname: "", // XXX: Should we set this?
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: func(format string, v ...interface{}) {
|
||||
Logf("world: etcd: "+format, v...)
|
||||
},
|
||||
}
|
||||
if err := world.Connect(ctx, worldInit); err != nil {
|
||||
return false, errwrap.Wrapf(err, "world Connect failed")
|
||||
}
|
||||
defer func() {
|
||||
err := errwrap.Wrapf(world.Cleanup(), "world Cleanup failed")
|
||||
if err != nil {
|
||||
// TODO: cause the final exit code to be non-zero?
|
||||
Logf("close error: %+v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// get max id (from all the previous deploys)
|
||||
max, err := world.GetMaxDeployID(ctx)
|
||||
if err != nil {
|
||||
return false, errwrap.Wrapf(err, "error getting max deploy id")
|
||||
}
|
||||
// find the latest id
|
||||
var id = max + 1 // next id
|
||||
Logf("previous max deploy id: %d", max)
|
||||
|
||||
// XXX: Get this from the World API? (Which might need improving!)
|
||||
etcdFs := &etcdfs.Fs{
|
||||
Client: etcdClient,
|
||||
// TODO: using a uuid is meant as a temporary measure, i hate them
|
||||
Metadata: lib.MetadataPrefix + fmt.Sprintf("/deploy/%d-%s", id, uniqueid),
|
||||
DataPrefix: lib.StoragePrefix,
|
||||
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: func(format string, v ...interface{}) {
|
||||
Logf("fs: "+format, v...)
|
||||
},
|
||||
}
|
||||
|
||||
info := &gapi.Info{
|
||||
Args: args,
|
||||
Flags: &gapi.Flags{
|
||||
Noop: obj.Noop,
|
||||
Sema: obj.Sema,
|
||||
//Update: obj.Update,
|
||||
},
|
||||
|
||||
Fs: etcdFs,
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: func(format string, v ...interface{}) {
|
||||
// TODO: is this a sane prefix to use here?
|
||||
data.Flags.Logf("cli: "+format, v...)
|
||||
},
|
||||
}
|
||||
|
||||
deploy, err := gapiObj.Cli(info)
|
||||
if err != nil {
|
||||
return false, cliUtil.CliParseError(err) // consistent errors
|
||||
}
|
||||
if deploy == nil { // not used
|
||||
return false, fmt.Errorf("not enough information specified")
|
||||
}
|
||||
|
||||
// redundant
|
||||
deploy.Noop = obj.Noop
|
||||
deploy.Sema = obj.Sema
|
||||
|
||||
deploy.NoAutoEdges = obj.NoAutoEdges
|
||||
|
||||
str, err := deploy.ToB64()
|
||||
if err != nil {
|
||||
return false, errwrap.Wrapf(err, "encoding error")
|
||||
}
|
||||
|
||||
Logf("pushing...")
|
||||
// this nominally checks the previous git hash matches our expectation
|
||||
if err := world.AddDeploy(ctx, id, hash, pHash, &str); err != nil {
|
||||
return false, errwrap.Wrapf(err, "could not create deploy id `%d`", id)
|
||||
}
|
||||
Logf("success, id: %d", id)
|
||||
return true, nil
|
||||
}
|
||||
150
cli/docs.go
Normal file
150
cli/docs.go
Normal file
@@ -0,0 +1,150 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
cliUtil "github.com/purpleidea/mgmt/cli/util"
|
||||
"github.com/purpleidea/mgmt/docs"
|
||||
)
|
||||
|
||||
// DocsGenerateArgs is the CLI parsing structure and type of the parsed result.
|
||||
// This particular one contains all the common flags for the `docs generate`
|
||||
// subcommand.
|
||||
type DocsGenerateArgs struct {
|
||||
docs.Config // embedded config (can't be a pointer) https://github.com/alexflint/go-arg/issues/240
|
||||
|
||||
DocsGenerate *cliUtil.DocsGenerateArgs `arg:"subcommand:generate" help:"generate documentation"`
|
||||
}
|
||||
|
||||
// Run executes the correct subcommand. It errors if there's ever an error. It
|
||||
// returns true if we did activate one of the subcommands. It returns false if
|
||||
// we did not. This information is used so that the top-level parser can return
|
||||
// usage or help information if no subcommand activates. This particular Run is
|
||||
// the run for the main `docs` subcommand.
|
||||
func (obj *DocsGenerateArgs) Run(ctx context.Context, data *cliUtil.Data) (bool, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
var name string
|
||||
var args interface{}
|
||||
if cmd := obj.DocsGenerate; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "generate"
|
||||
args = cmd
|
||||
}
|
||||
_ = name
|
||||
|
||||
Logf := func(format string, v ...interface{}) {
|
||||
// Don't block this globally...
|
||||
//if !data.Flags.Debug {
|
||||
// return
|
||||
//}
|
||||
data.Flags.Logf("main: "+format, v...)
|
||||
}
|
||||
|
||||
var api docs.API
|
||||
|
||||
if cmd := obj.DocsGenerate; cmd != nil {
|
||||
api = &docs.Generate{
|
||||
DocsGenerateArgs: args.(*cliUtil.DocsGenerateArgs),
|
||||
Config: obj.Config,
|
||||
Program: data.Program,
|
||||
Version: data.Version,
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: Logf,
|
||||
}
|
||||
}
|
||||
|
||||
if api == nil {
|
||||
return false, nil // nothing found (display help!)
|
||||
}
|
||||
|
||||
// We don't use these for the setup command in normal operation.
|
||||
if data.Flags.Debug {
|
||||
cliUtil.Hello(data.Program, data.Version, data.Flags) // say hello!
|
||||
defer Logf("goodbye!")
|
||||
}
|
||||
|
||||
// install the exit signal handler
|
||||
wg := &sync.WaitGroup{}
|
||||
defer wg.Wait()
|
||||
exit := make(chan struct{})
|
||||
defer close(exit)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer cancel()
|
||||
defer wg.Done()
|
||||
// must have buffer for max number of signals
|
||||
signals := make(chan os.Signal, 3+1) // 3 * ^C + 1 * SIGTERM
|
||||
signal.Notify(signals, os.Interrupt) // catch ^C
|
||||
//signal.Notify(signals, os.Kill) // catch signals
|
||||
signal.Notify(signals, syscall.SIGTERM)
|
||||
var count uint8
|
||||
for {
|
||||
select {
|
||||
case sig := <-signals: // any signal will do
|
||||
if sig != os.Interrupt {
|
||||
data.Flags.Logf("interrupted by signal")
|
||||
return
|
||||
}
|
||||
|
||||
switch count {
|
||||
case 0:
|
||||
data.Flags.Logf("interrupted by ^C")
|
||||
cancel()
|
||||
case 1:
|
||||
data.Flags.Logf("interrupted by ^C (fast pause)")
|
||||
cancel()
|
||||
case 2:
|
||||
data.Flags.Logf("interrupted by ^C (hard interrupt)")
|
||||
cancel()
|
||||
}
|
||||
count++
|
||||
|
||||
case <-exit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := api.Main(ctx); err != nil {
|
||||
if data.Flags.Debug {
|
||||
data.Flags.Logf("main: %+v", err)
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
151
cli/firstboot.go
Normal file
151
cli/firstboot.go
Normal file
@@ -0,0 +1,151 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
cliUtil "github.com/purpleidea/mgmt/cli/util"
|
||||
"github.com/purpleidea/mgmt/firstboot"
|
||||
)
|
||||
|
||||
// FirstbootArgs is the CLI parsing structure and type of the parsed result.
|
||||
// This particular one contains all the common flags for the `firstboot`
|
||||
// subcommand.
|
||||
type FirstbootArgs struct {
|
||||
firstboot.Config // embedded config (can't be a pointer) https://github.com/alexflint/go-arg/issues/240
|
||||
|
||||
FirstbootStart *cliUtil.FirstbootStartArgs `arg:"subcommand:start" help:"start firstboot service"`
|
||||
}
|
||||
|
||||
// Run executes the correct subcommand. It errors if there's ever an error. It
|
||||
// returns true if we did activate one of the subcommands. It returns false if
|
||||
// we did not. This information is used so that the top-level parser can return
|
||||
// usage or help information if no subcommand activates. This particular Run is
|
||||
// the run for the main `firstboot` subcommand. The firstboot command as a
|
||||
// service that lets you run commands once on the first boot of a system.
|
||||
func (obj *FirstbootArgs) Run(ctx context.Context, data *cliUtil.Data) (bool, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
var name string
|
||||
var args interface{}
|
||||
if cmd := obj.FirstbootStart; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "pkg"
|
||||
args = cmd
|
||||
}
|
||||
_ = name
|
||||
|
||||
Logf := func(format string, v ...interface{}) {
|
||||
// Don't block this globally...
|
||||
//if !data.Flags.Debug {
|
||||
// return
|
||||
//}
|
||||
data.Flags.Logf("main: "+format, v...)
|
||||
}
|
||||
|
||||
var api firstboot.API
|
||||
|
||||
if cmd := obj.FirstbootStart; cmd != nil {
|
||||
api = &firstboot.Start{
|
||||
FirstbootStartArgs: args.(*cliUtil.FirstbootStartArgs),
|
||||
Config: obj.Config,
|
||||
Program: data.Program,
|
||||
Version: data.Version,
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: Logf,
|
||||
}
|
||||
}
|
||||
|
||||
if api == nil {
|
||||
return false, nil // nothing found (display help!)
|
||||
}
|
||||
|
||||
// We don't use these for the setup command in normal operation.
|
||||
if data.Flags.Debug {
|
||||
cliUtil.Hello(data.Program, data.Version, data.Flags) // say hello!
|
||||
defer Logf("goodbye!")
|
||||
}
|
||||
|
||||
// install the exit signal handler
|
||||
wg := &sync.WaitGroup{}
|
||||
defer wg.Wait()
|
||||
exit := make(chan struct{})
|
||||
defer close(exit)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer cancel()
|
||||
defer wg.Done()
|
||||
// must have buffer for max number of signals
|
||||
signals := make(chan os.Signal, 3+1) // 3 * ^C + 1 * SIGTERM
|
||||
signal.Notify(signals, os.Interrupt) // catch ^C
|
||||
//signal.Notify(signals, os.Kill) // catch signals
|
||||
signal.Notify(signals, syscall.SIGTERM)
|
||||
var count uint8
|
||||
for {
|
||||
select {
|
||||
case sig := <-signals: // any signal will do
|
||||
if sig != os.Interrupt {
|
||||
data.Flags.Logf("interrupted by signal")
|
||||
return
|
||||
}
|
||||
|
||||
switch count {
|
||||
case 0:
|
||||
data.Flags.Logf("interrupted by ^C")
|
||||
cancel()
|
||||
case 1:
|
||||
data.Flags.Logf("interrupted by ^C (fast pause)")
|
||||
cancel()
|
||||
case 2:
|
||||
data.Flags.Logf("interrupted by ^C (hard interrupt)")
|
||||
cancel()
|
||||
}
|
||||
count++
|
||||
|
||||
case <-exit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := api.Main(ctx); err != nil {
|
||||
if data.Flags.Debug {
|
||||
data.Flags.Logf("main: %+v", err)
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
245
cli/run.go
Normal file
245
cli/run.go
Normal file
@@ -0,0 +1,245 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
cliUtil "github.com/purpleidea/mgmt/cli/util"
|
||||
"github.com/purpleidea/mgmt/gapi"
|
||||
"github.com/purpleidea/mgmt/lib"
|
||||
"github.com/purpleidea/mgmt/util"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
// RunArgs is the CLI parsing structure and type of the parsed result. This
|
||||
// particular one contains all the common flags for the `run` subcommand which
|
||||
// all frontends can use.
|
||||
type RunArgs struct {
|
||||
lib.Config // embedded config (can't be a pointer) https://github.com/alexflint/go-arg/issues/240
|
||||
|
||||
RunEmpty *cliUtil.EmptyArgs `arg:"subcommand:empty" help:"run empty payload"`
|
||||
RunLang *cliUtil.LangArgs `arg:"subcommand:lang" help:"run lang (mcl) payload"`
|
||||
RunYaml *cliUtil.YamlArgs `arg:"subcommand:yaml" help:"run yaml graph payload"`
|
||||
RunPuppet *cliUtil.PuppetArgs `arg:"subcommand:puppet" help:"run puppet graph payload"`
|
||||
RunLangPuppet *cliUtil.LangPuppetArgs `arg:"subcommand:langpuppet" help:"run a combined lang/puppet graph payload"`
|
||||
}
|
||||
|
||||
// Run executes the correct subcommand. It errors if there's ever an error. It
|
||||
// returns true if we did activate one of the subcommands. It returns false if
|
||||
// we did not. This information is used so that the top-level parser can return
|
||||
// usage or help information if no subcommand activates. This particular Run is
|
||||
// the run for the main `run` subcommand. This always requires a frontend to
|
||||
// start the engine, but if you don't want a graph, you can use the `empty`
|
||||
// frontend. The engine backend is agnostic to which frontend is running, in
|
||||
// fact, you can deploy with multiple different frontends, one after another, on
|
||||
// the same engine.
|
||||
func (obj *RunArgs) Run(ctx context.Context, data *cliUtil.Data) (bool, error) {
|
||||
var name string
|
||||
var args interface{}
|
||||
if cmd := obj.RunEmpty; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "empty"
|
||||
args = cmd
|
||||
}
|
||||
if cmd := obj.RunLang; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "lang"
|
||||
args = cmd
|
||||
}
|
||||
if cmd := obj.RunYaml; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "yaml"
|
||||
args = cmd
|
||||
}
|
||||
if cmd := obj.RunPuppet; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "puppet"
|
||||
args = cmd
|
||||
}
|
||||
if cmd := obj.RunLangPuppet; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "langpuppet"
|
||||
args = cmd
|
||||
}
|
||||
|
||||
// XXX: workaround https://github.com/alexflint/go-arg/issues/239
|
||||
lists := [][]string{
|
||||
obj.Seeds,
|
||||
obj.ClientURLs,
|
||||
obj.ServerURLs,
|
||||
obj.AdvertiseClientURLs,
|
||||
obj.AdvertiseServerURLs,
|
||||
}
|
||||
gapiNames := gapi.Names() // list of registered names
|
||||
for _, list := range lists {
|
||||
if l := len(list); name == "" && l > 1 {
|
||||
elem := list[l-2] // second to last element
|
||||
if util.StrInList(elem, gapiNames) {
|
||||
return false, cliUtil.CliParseError(cliUtil.MissingEquals) // consistent errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn, exists := gapi.RegisteredGAPIs[name]
|
||||
if !exists {
|
||||
return false, nil // did not activate
|
||||
}
|
||||
gapiObj := fn()
|
||||
|
||||
main := &lib.Main{}
|
||||
main.Config = &obj.Config // pass in all the parsed data
|
||||
|
||||
main.Program, main.Version = data.Program, data.Version
|
||||
main.Debug, main.Logf = data.Flags.Debug, data.Flags.Logf // no prefix
|
||||
Logf := func(format string, v ...interface{}) {
|
||||
data.Flags.Logf("main: "+format, v...)
|
||||
}
|
||||
|
||||
cliUtil.Hello(main.Program, main.Version, data.Flags) // say hello!
|
||||
defer Logf("goodbye!")
|
||||
|
||||
// create a memory backed temporary filesystem for storing runtime data
|
||||
mmFs := afero.NewMemMapFs()
|
||||
afs := &afero.Afero{Fs: mmFs} // wrap so that we're implementing ioutil
|
||||
standaloneFs := &util.AferoFs{Afero: afs}
|
||||
main.DeployFs = standaloneFs
|
||||
|
||||
info := &gapi.Info{
|
||||
Args: args,
|
||||
Flags: &gapi.Flags{
|
||||
Hostname: obj.Hostname,
|
||||
Noop: obj.Noop,
|
||||
Sema: obj.Sema,
|
||||
//Update: obj.Update,
|
||||
|
||||
NoAutoEdges: obj.NoAutoEdges,
|
||||
},
|
||||
|
||||
Fs: standaloneFs,
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: func(format string, v ...interface{}) {
|
||||
data.Flags.Logf("cli: "+format, v...)
|
||||
},
|
||||
}
|
||||
|
||||
deploy, err := gapiObj.Cli(info)
|
||||
if err != nil {
|
||||
return false, cliUtil.CliParseError(err) // consistent errors
|
||||
}
|
||||
|
||||
if cmd := obj.RunLang; cmd != nil && cmd.OnlyUnify && deploy == nil {
|
||||
return true, nil // we end early
|
||||
}
|
||||
if cmd := obj.RunLang; cmd != nil && cmd.OnlyDownload && deploy == nil {
|
||||
return true, nil // we end early
|
||||
}
|
||||
main.Deploy = deploy
|
||||
if main.Deploy == nil {
|
||||
// nobody activated, but we'll still watch the etcd deploy chan,
|
||||
// and if there is deployed code that's ready to run, we'll run!
|
||||
data.Flags.Logf("main: no frontend selected (no GAPI activated)")
|
||||
}
|
||||
|
||||
if err := main.Validate(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := main.Init(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// install the exit signal handler
|
||||
wg := &sync.WaitGroup{}
|
||||
defer wg.Wait()
|
||||
exit := make(chan struct{})
|
||||
defer close(exit)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// must have buffer for max number of signals
|
||||
signals := make(chan os.Signal, 3+1) // 3 * ^C + 1 * SIGTERM
|
||||
signal.Notify(signals, os.Interrupt) // catch ^C
|
||||
//signal.Notify(signals, os.Kill) // catch signals
|
||||
signal.Notify(signals, syscall.SIGTERM)
|
||||
var count uint8
|
||||
for {
|
||||
select {
|
||||
case sig := <-signals: // any signal will do
|
||||
if sig != os.Interrupt {
|
||||
data.Flags.Logf("interrupted by signal")
|
||||
main.Interrupt(fmt.Errorf("killed by %v", sig))
|
||||
return
|
||||
}
|
||||
|
||||
switch count {
|
||||
case 0:
|
||||
data.Flags.Logf("interrupted by ^C")
|
||||
main.Exit(nil)
|
||||
case 1:
|
||||
data.Flags.Logf("interrupted by ^C (fast pause)")
|
||||
main.FastExit(nil)
|
||||
case 2:
|
||||
data.Flags.Logf("interrupted by ^C (hard interrupt)")
|
||||
main.Interrupt(nil)
|
||||
}
|
||||
count++
|
||||
|
||||
case <-exit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
reterr := main.Run()
|
||||
if reterr != nil {
|
||||
// log the error message returned
|
||||
if data.Flags.Debug {
|
||||
data.Flags.Logf("main: %+v", reterr)
|
||||
}
|
||||
}
|
||||
|
||||
if err := main.Close(); err != nil {
|
||||
if data.Flags.Debug {
|
||||
data.Flags.Logf("main: Close: %+v", err)
|
||||
}
|
||||
if reterr == nil {
|
||||
return false, err
|
||||
}
|
||||
reterr = errwrap.Append(reterr, err)
|
||||
}
|
||||
|
||||
if reterr != nil {
|
||||
return false, reterr
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
180
cli/setup.go
Normal file
180
cli/setup.go
Normal file
@@ -0,0 +1,180 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
cliUtil "github.com/purpleidea/mgmt/cli/util"
|
||||
"github.com/purpleidea/mgmt/setup"
|
||||
)
|
||||
|
||||
// SetupArgs is the CLI parsing structure and type of the parsed result. This
|
||||
// particular one contains all the common flags for the `setup` subcommand.
|
||||
type SetupArgs struct {
|
||||
setup.Config // embedded config (can't be a pointer) https://github.com/alexflint/go-arg/issues/240
|
||||
|
||||
SetupPkg *cliUtil.SetupPkgArgs `arg:"subcommand:pkg" help:"setup packages"`
|
||||
SetupSvc *cliUtil.SetupSvcArgs `arg:"subcommand:svc" help:"setup services"`
|
||||
SetupFirstboot *cliUtil.SetupFirstbootArgs `arg:"subcommand:firstboot" help:"setup firstboot"`
|
||||
}
|
||||
|
||||
// Run executes the correct subcommand. It errors if there's ever an error. It
|
||||
// returns true if we did activate one of the subcommands. It returns false if
|
||||
// we did not. This information is used so that the top-level parser can return
|
||||
// usage or help information if no subcommand activates. This particular Run is
|
||||
// the run for the main `setup` subcommand. The setup command does some
|
||||
// bootstrap work to help get things going.
|
||||
func (obj *SetupArgs) Run(ctx context.Context, data *cliUtil.Data) (bool, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
var name string
|
||||
var args interface{}
|
||||
if cmd := obj.SetupPkg; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "pkg"
|
||||
args = cmd
|
||||
}
|
||||
if cmd := obj.SetupSvc; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "svc"
|
||||
args = cmd
|
||||
}
|
||||
if cmd := obj.SetupFirstboot; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "firstboot"
|
||||
args = cmd
|
||||
}
|
||||
_ = name
|
||||
|
||||
Logf := func(format string, v ...interface{}) {
|
||||
// Don't block this globally...
|
||||
//if !data.Flags.Debug {
|
||||
// return
|
||||
//}
|
||||
data.Flags.Logf("main: "+format, v...)
|
||||
}
|
||||
|
||||
var api setup.API
|
||||
|
||||
if cmd := obj.SetupPkg; cmd != nil {
|
||||
api = &setup.Pkg{
|
||||
SetupPkgArgs: args.(*cliUtil.SetupPkgArgs),
|
||||
Config: obj.Config,
|
||||
Program: data.Program,
|
||||
Version: data.Version,
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: Logf,
|
||||
}
|
||||
}
|
||||
if cmd := obj.SetupSvc; cmd != nil {
|
||||
api = &setup.Svc{
|
||||
SetupSvcArgs: args.(*cliUtil.SetupSvcArgs),
|
||||
Config: obj.Config,
|
||||
Program: data.Program,
|
||||
Version: data.Version,
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: Logf,
|
||||
}
|
||||
}
|
||||
if cmd := obj.SetupFirstboot; cmd != nil {
|
||||
api = &setup.Firstboot{
|
||||
SetupFirstbootArgs: args.(*cliUtil.SetupFirstbootArgs),
|
||||
Config: obj.Config,
|
||||
Program: data.Program,
|
||||
Version: data.Version,
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: Logf,
|
||||
}
|
||||
}
|
||||
|
||||
if api == nil {
|
||||
return false, nil // nothing found (display help!)
|
||||
}
|
||||
|
||||
// We don't use these for the setup command in normal operation.
|
||||
if data.Flags.Debug {
|
||||
cliUtil.Hello(data.Program, data.Version, data.Flags) // say hello!
|
||||
defer Logf("goodbye!")
|
||||
}
|
||||
|
||||
// install the exit signal handler
|
||||
wg := &sync.WaitGroup{}
|
||||
defer wg.Wait()
|
||||
exit := make(chan struct{})
|
||||
defer close(exit)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer cancel()
|
||||
defer wg.Done()
|
||||
// must have buffer for max number of signals
|
||||
signals := make(chan os.Signal, 3+1) // 3 * ^C + 1 * SIGTERM
|
||||
signal.Notify(signals, os.Interrupt) // catch ^C
|
||||
//signal.Notify(signals, os.Kill) // catch signals
|
||||
signal.Notify(signals, syscall.SIGTERM)
|
||||
var count uint8
|
||||
for {
|
||||
select {
|
||||
case sig := <-signals: // any signal will do
|
||||
if sig != os.Interrupt {
|
||||
data.Flags.Logf("interrupted by signal")
|
||||
return
|
||||
}
|
||||
|
||||
switch count {
|
||||
case 0:
|
||||
data.Flags.Logf("interrupted by ^C")
|
||||
cancel()
|
||||
case 1:
|
||||
data.Flags.Logf("interrupted by ^C (fast pause)")
|
||||
cancel()
|
||||
case 2:
|
||||
data.Flags.Logf("interrupted by ^C (hard interrupt)")
|
||||
cancel()
|
||||
}
|
||||
count++
|
||||
|
||||
case <-exit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := api.Main(ctx); err != nil {
|
||||
if data.Flags.Debug {
|
||||
data.Flags.Logf("main: %+v", err)
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
150
cli/tools.go
Normal file
150
cli/tools.go
Normal file
@@ -0,0 +1,150 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
cliUtil "github.com/purpleidea/mgmt/cli/util"
|
||||
"github.com/purpleidea/mgmt/tools"
|
||||
)
|
||||
|
||||
// ToolsArgs is the CLI parsing structure and type of the parsed result. This
|
||||
// particular one contains all the common flags for the `tools` subcommand.
|
||||
type ToolsArgs struct {
|
||||
tools.Config // embedded config (can't be a pointer) https://github.com/alexflint/go-arg/issues/240
|
||||
|
||||
ToolsGrow *cliUtil.ToolsGrowArgs `arg:"subcommand:grow" help:"tools for growing storage"`
|
||||
}
|
||||
|
||||
// Run executes the correct subcommand. It errors if there's ever an error. It
|
||||
// returns true if we did activate one of the subcommands. It returns false if
|
||||
// we did not. This information is used so that the top-level parser can return
|
||||
// usage or help information if no subcommand activates. This particular Run is
|
||||
// the run for the main `tools` subcommand. The tools command provides some
|
||||
// functionality which can be helpful with provisioning and config management.
|
||||
func (obj *ToolsArgs) Run(ctx context.Context, data *cliUtil.Data) (bool, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
var name string
|
||||
var args interface{}
|
||||
if cmd := obj.ToolsGrow; cmd != nil {
|
||||
name = cliUtil.LookupSubcommand(obj, cmd) // "grow"
|
||||
args = cmd
|
||||
}
|
||||
_ = name
|
||||
|
||||
Logf := func(format string, v ...interface{}) {
|
||||
// Don't block this globally...
|
||||
//if !data.Flags.Debug {
|
||||
// return
|
||||
//}
|
||||
data.Flags.Logf("main: "+format, v...)
|
||||
}
|
||||
|
||||
var api tools.API
|
||||
|
||||
if cmd := obj.ToolsGrow; cmd != nil {
|
||||
api = &tools.Grow{
|
||||
ToolsGrowArgs: args.(*cliUtil.ToolsGrowArgs),
|
||||
Config: obj.Config,
|
||||
Program: data.Program,
|
||||
Version: data.Version,
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: Logf,
|
||||
}
|
||||
}
|
||||
|
||||
if api == nil {
|
||||
return false, nil // nothing found (display help!)
|
||||
}
|
||||
|
||||
// We don't use these for the tools command in normal operation.
|
||||
if data.Flags.Debug {
|
||||
cliUtil.Hello(data.Program, data.Version, data.Flags) // say hello!
|
||||
defer Logf("goodbye!")
|
||||
}
|
||||
|
||||
// install the exit signal handler
|
||||
wg := &sync.WaitGroup{}
|
||||
defer wg.Wait()
|
||||
exit := make(chan struct{})
|
||||
defer close(exit)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer cancel()
|
||||
defer wg.Done()
|
||||
// must have buffer for max number of signals
|
||||
signals := make(chan os.Signal, 3+1) // 3 * ^C + 1 * SIGTERM
|
||||
signal.Notify(signals, os.Interrupt) // catch ^C
|
||||
//signal.Notify(signals, os.Kill) // catch signals
|
||||
signal.Notify(signals, syscall.SIGTERM)
|
||||
var count uint8
|
||||
for {
|
||||
select {
|
||||
case sig := <-signals: // any signal will do
|
||||
if sig != os.Interrupt {
|
||||
data.Flags.Logf("interrupted by signal")
|
||||
return
|
||||
}
|
||||
|
||||
switch count {
|
||||
case 0:
|
||||
data.Flags.Logf("interrupted by ^C")
|
||||
cancel()
|
||||
case 1:
|
||||
data.Flags.Logf("interrupted by ^C (fast pause)")
|
||||
cancel()
|
||||
case 2:
|
||||
data.Flags.Logf("interrupted by ^C (hard interrupt)")
|
||||
cancel()
|
||||
}
|
||||
count++
|
||||
|
||||
case <-exit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := api.Main(ctx); err != nil {
|
||||
if data.Flags.Debug {
|
||||
data.Flags.Logf("main: %+v", err)
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
214
cli/util/args.go
Normal file
214
cli/util/args.go
Normal file
@@ -0,0 +1,214 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// LookupSubcommand returns the name of the subcommand in the obj, of a struct.
|
||||
// This is useful for determining the name of the subcommand that was activated.
|
||||
// It returns an empty string if a specific name was not found.
|
||||
func LookupSubcommand(obj interface{}, st interface{}) string {
|
||||
val := reflect.ValueOf(obj)
|
||||
if val.Kind() == reflect.Ptr { // max one de-referencing
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(st) // value of the struct
|
||||
typ := val.Type()
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
f := val.Field(i) // value of the field
|
||||
if f.Interface() != v.Interface() {
|
||||
continue
|
||||
}
|
||||
|
||||
field := typ.Field(i)
|
||||
alias, ok := field.Tag.Lookup("arg")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// XXX: `arg` needs a split by comma first or fancier parsing
|
||||
prefix := "subcommand"
|
||||
split := strings.Split(alias, ":")
|
||||
if len(split) != 2 || split[0] != prefix {
|
||||
continue
|
||||
}
|
||||
|
||||
return split[1] // found
|
||||
}
|
||||
return "" // not found
|
||||
}
|
||||
|
||||
// EmptyArgs is the empty CLI parsing structure and type of the parsed result.
|
||||
type EmptyArgs struct {
|
||||
Wait bool `arg:"--wait" help:"don't use any existing (stale) deploys"`
|
||||
}
|
||||
|
||||
// LangArgs is the lang CLI parsing structure and type of the parsed result.
|
||||
type LangArgs struct {
|
||||
// Input is the input mcl code or file path or any input specification.
|
||||
Input string `arg:"positional,required"`
|
||||
|
||||
// TODO: removed (temporarily?)
|
||||
//Stdin bool `arg:"--stdin" help:"use passthrough stdin"`
|
||||
|
||||
Download bool `arg:"--download" help:"download any missing imports"`
|
||||
OnlyDownload bool `arg:"--only-download" help:"stop after downloading any missing imports"`
|
||||
Update bool `arg:"--update" help:"update all dependencies to the latest versions"`
|
||||
|
||||
OnlyUnify bool `arg:"--only-unify" help:"stop after type unification"`
|
||||
SkipUnify bool `arg:"--skip-unify" help:"skip type unification"`
|
||||
UnifySolver *string `arg:"--unify-name" help:"pick a specific unification solver"`
|
||||
UnifyOptimizations []string `arg:"--unify-optimizations,separate" help:"list of unification optimizations to request (experts only)"`
|
||||
|
||||
Depth int `arg:"--depth" default:"-1" help:"max recursion depth limit (-1 is unlimited)"`
|
||||
|
||||
// The default of 0 means any error is a failure by default.
|
||||
Retry int `arg:"--depth" help:"max number of retries (-1 is unlimited)"`
|
||||
|
||||
ModulePath string `arg:"--module-path,env:MGMT_MODULE_PATH" help:"choose the modules path (absolute)"`
|
||||
}
|
||||
|
||||
// YamlArgs is the yaml CLI parsing structure and type of the parsed result.
|
||||
type YamlArgs struct {
|
||||
// Input is the input yaml code or file path or any input specification.
|
||||
Input string `arg:"positional,required"`
|
||||
}
|
||||
|
||||
// PuppetArgs is the puppet CLI parsing structure and type of the parsed result.
|
||||
type PuppetArgs struct {
|
||||
// Input is the input puppet code or file path or just "agent".
|
||||
Input string `arg:"positional,required"`
|
||||
|
||||
// PuppetConf is the optional path to a puppet.conf config file.
|
||||
PuppetConf string `arg:"--puppet-conf" help:"full path to the puppet.conf file to use"`
|
||||
}
|
||||
|
||||
// LangPuppetArgs is the langpuppet CLI parsing structure and type of the parsed
|
||||
// result.
|
||||
type LangPuppetArgs struct {
|
||||
// LangInput is the input mcl code or file path or any input specification.
|
||||
LangInput string `arg:"--lang,required" help:"the input parameter for the lang module"`
|
||||
|
||||
// PuppetInput is the input puppet code or file path or just "agent".
|
||||
PuppetInput string `arg:"--puppet,required" help:"the input parameter for the puppet module"`
|
||||
|
||||
// copy-pasted from PuppetArgs
|
||||
|
||||
// PuppetConf is the optional path to a puppet.conf config file.
|
||||
PuppetConf string `arg:"--puppet-conf" help:"full path to the puppet.conf file to use"`
|
||||
|
||||
// end PuppetArgs
|
||||
|
||||
// copy-pasted from LangArgs
|
||||
|
||||
// TODO: removed (temporarily?)
|
||||
//Stdin bool `arg:"--stdin" help:"use passthrough stdin"`
|
||||
|
||||
Download bool `arg:"--download" help:"download any missing imports"`
|
||||
OnlyDownload bool `arg:"--only-download" help:"stop after downloading any missing imports"`
|
||||
Update bool `arg:"--update" help:"update all dependencies to the latest versions"`
|
||||
|
||||
OnlyUnify bool `arg:"--only-unify" help:"stop after type unification"`
|
||||
SkipUnify bool `arg:"--skip-unify" help:"skip type unification"`
|
||||
|
||||
Depth int `arg:"--depth" default:"-1" help:"max recursion depth limit (-1 is unlimited)"`
|
||||
|
||||
// The default of 0 means any error is a failure by default.
|
||||
Retry int `arg:"--depth" help:"max number of retries (-1 is unlimited)"`
|
||||
|
||||
ModulePath string `arg:"--module-path,env:MGMT_MODULE_PATH" help:"choose the modules path (absolute)"`
|
||||
|
||||
// end LangArgs
|
||||
}
|
||||
|
||||
// SetupPkgArgs is the setup service CLI parsing structure and type of the
|
||||
// parsed result.
|
||||
type SetupPkgArgs struct {
|
||||
Distro string `arg:"--distro" help:"build for this distro"`
|
||||
Sudo bool `arg:"--sudo" help:"include sudo in the command"`
|
||||
Exec bool `arg:"--exec" help:"actually run these commands"`
|
||||
}
|
||||
|
||||
// SetupSvcArgs is the setup service CLI parsing structure and type of the
|
||||
// parsed result.
|
||||
type SetupSvcArgs struct {
|
||||
BinaryPath string `arg:"--binary-path" help:"path to the binary"`
|
||||
SSHURL string `arg:"--ssh-url" help:"transport the etcd client connection over SSH to this server"`
|
||||
SSHHostKey string `arg:"--ssh-hostkey" help:"use this ssh known hosts key when connecting over SSH"`
|
||||
|
||||
Seeds []string `arg:"--seeds,separate,env:MGMT_SEEDS" help:"default etcd client endpoints"`
|
||||
NoServer bool `arg:"--no-server" help:"do not start embedded etcd server (do not promote from client to peer)"`
|
||||
|
||||
Install bool `arg:"--install" help:"install the systemd mgmt service"`
|
||||
Start bool `arg:"--start" help:"start the mgmt service"`
|
||||
Enable bool `arg:"--enable" help:"enable the mgmt service"`
|
||||
}
|
||||
|
||||
// SetupFirstbootArgs is the setup service CLI parsing structure and type of the
|
||||
// parsed result.
|
||||
type SetupFirstbootArgs struct {
|
||||
BinaryPath string `arg:"--binary-path" help:"path to the binary"`
|
||||
Mkdir bool `arg:"--mkdir" help:"make the necessary firstboot dirs"`
|
||||
Install bool `arg:"--install" help:"install the systemd firstboot service"`
|
||||
Start bool `arg:"--start" help:"start the firstboot service (typically not used)"`
|
||||
Enable bool `arg:"--enable" help:"enable the firstboot service"`
|
||||
|
||||
FirstbootStartArgs // Include these options if we want to specify them.
|
||||
}
|
||||
|
||||
// FirstbootStartArgs is the firstboot service CLI parsing structure and type of
|
||||
// the parsed result.
|
||||
type FirstbootStartArgs struct {
|
||||
LockFilePath string `arg:"--lock-file-path" help:"path to the lock file"`
|
||||
ScriptsDir string `arg:"--scripts-dir" help:"path to the scripts dir"`
|
||||
DoneDir string `arg:"--done-dir" help:"dir to move done scripts to"`
|
||||
LoggingDir string `arg:"--logging-dir" help:"directory to store logs in"`
|
||||
}
|
||||
|
||||
// DocsGenerateArgs is the docgen utility CLI parsing structure and type of the
|
||||
// parsed result.
|
||||
type DocsGenerateArgs struct {
|
||||
Output string `arg:"--output" help:"output path to write to"`
|
||||
RootDir string `arg:"--root-dir" help:"path to mgmt source dir"`
|
||||
NoResources bool `arg:"--no-resources" help:"skip resource doc generation"`
|
||||
NoFunctions bool `arg:"--no-functions" help:"skip function doc generation"`
|
||||
}
|
||||
|
||||
// ToolsGrowArgs is the util tool CLI parsing structure and type of the parsed
|
||||
// result.
|
||||
type ToolsGrowArgs struct {
|
||||
Mount string `arg:"--mount,required" help:"root mount point to start with"`
|
||||
Exec bool `arg:"--exec" help:"actually run these commands"`
|
||||
Done string `arg:"--done" help:"create this file when done, skip if it exists"`
|
||||
}
|
||||
47
cli/util/hello.go
Normal file
47
cli/util/hello.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Hello is a simple helper function to print a hello message and time.
|
||||
func Hello(program, version string, flags Flags) {
|
||||
var start = time.Now().UnixNano()
|
||||
if program == "" {
|
||||
program = "<unknown>"
|
||||
}
|
||||
fmt.Println(fmt.Sprintf("This is: %s, version: %s", program, version))
|
||||
fmt.Println("Copyright (C) James Shubin and the project contributors")
|
||||
fmt.Println("Written by James Shubin <james@shubin.ca> and the project contributors")
|
||||
flags.Logf("main: start: %v", start)
|
||||
}
|
||||
98
cli/util/util.go
Normal file
98
cli/util/util.go
Normal file
@@ -0,0 +1,98 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
// Package util has some CLI related utility code.
|
||||
package util
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
)
|
||||
|
||||
// Error is a constant error type that implements error.
|
||||
type Error string
|
||||
|
||||
// Error fulfills the error interface of this type.
|
||||
func (e Error) Error() string { return string(e) }
|
||||
|
||||
const (
|
||||
// MissingEquals means we probably hit the parsing bug.
|
||||
// XXX: see: https://github.com/alexflint/go-arg/issues/239
|
||||
MissingEquals = Error("missing equals sign for list element")
|
||||
)
|
||||
|
||||
// CliParseError returns a consistent error if we have a CLI parsing issue.
|
||||
func CliParseError(err error) error {
|
||||
return errwrap.Wrapf(err, "cli parse error")
|
||||
}
|
||||
|
||||
// Flags are some constant flags which are used throughout the program.
|
||||
type Flags struct {
|
||||
Debug bool // add additional log messages
|
||||
Logf func(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// Data is a struct of values that we usually pass to the main CLI function.
|
||||
type Data struct {
|
||||
Program string
|
||||
Version string
|
||||
Copying string
|
||||
Tagline string
|
||||
Flags Flags
|
||||
Args []string // os.Args usually
|
||||
}
|
||||
|
||||
// SafeProgram returns the correct program string when given a buggy variant.
|
||||
func SafeProgram(program string) string {
|
||||
// FIXME: in sub commands, the cli package appends a space and the sub
|
||||
// command name at the end. hack around this by only using the first bit
|
||||
// see: https://github.com/urfave/cli/issues/783 for more details...
|
||||
split := strings.Split(program, " ")
|
||||
program = split[0]
|
||||
//if program == "" {
|
||||
// program = "<unknown>"
|
||||
//}
|
||||
return program
|
||||
}
|
||||
|
||||
// LogSetup changes some of the core logger package settings.
|
||||
func LogSetup(debug bool) {
|
||||
// TODO: Move these log package initialization steps to the top main.go?
|
||||
logFlags := log.LstdFlags
|
||||
if debug {
|
||||
logFlags = logFlags + log.Lshortfile
|
||||
}
|
||||
logFlags = logFlags - log.Ldate // remove the date for now
|
||||
log.SetFlags(logFlags)
|
||||
|
||||
log.SetOutput(os.Stderr)
|
||||
}
|
||||
@@ -1,153 +1,322 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2016+ James Shubin and the project contributors
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
// Package converger is a facility for reporting the converged state.
|
||||
package converger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/purpleidea/mgmt/util"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
)
|
||||
|
||||
// TODO: we could make a new function that masks out the state of certain
|
||||
// UID's, but at the moment the new Timer code has obsoleted the need...
|
||||
|
||||
// Converger is the general interface for implementing a convergence watcher
|
||||
type Converger interface { // TODO: need a better name
|
||||
Register() ConvergerUID
|
||||
IsConverged(ConvergerUID) bool // is the UID converged ?
|
||||
SetConverged(ConvergerUID, bool) error // set the converged state of the UID
|
||||
Unregister(ConvergerUID)
|
||||
Start()
|
||||
Pause()
|
||||
Loop(bool)
|
||||
ConvergedTimer(ConvergerUID) <-chan time.Time
|
||||
Status() map[uint64]bool
|
||||
Timeout() int // returns the timeout that this was created with
|
||||
SetStateFn(func(bool) error) // sets the stateFn
|
||||
}
|
||||
|
||||
// ConvergerUID is the interface resources can use to notify with if converged
|
||||
// you'll need to use part of the Converger interface to Register initially too
|
||||
type ConvergerUID interface {
|
||||
ID() uint64 // get Id
|
||||
Name() string // get a friendly name
|
||||
SetName(string)
|
||||
IsValid() bool // has Id been initialized ?
|
||||
InvalidateID() // set Id to nil
|
||||
IsConverged() bool
|
||||
SetConverged(bool) error
|
||||
Unregister()
|
||||
ConvergedTimer() <-chan time.Time
|
||||
StartTimer() (func() error, error) // cancellable is the same as StopTimer()
|
||||
ResetTimer() error // resets counter to zero
|
||||
StopTimer() error
|
||||
}
|
||||
|
||||
// converger is an implementation of the Converger interface
|
||||
type converger struct {
|
||||
timeout int // must be zero (instant) or greater seconds to run
|
||||
stateFn func(bool) error // run on converged state changes with state bool
|
||||
converged bool // did we converge (state changes of this run Fn)
|
||||
channel chan struct{} // signal here to run an isConverged check
|
||||
control chan bool // control channel for start/pause
|
||||
mutex sync.RWMutex // used for controlling access to status and lastid
|
||||
lastid uint64
|
||||
status map[uint64]bool
|
||||
}
|
||||
|
||||
// convergerUID is an implementation of the ConvergerUID interface
|
||||
type convergerUID struct {
|
||||
converger Converger
|
||||
id uint64
|
||||
name string // user defined, friendly name
|
||||
mutex sync.Mutex
|
||||
timer chan struct{}
|
||||
running bool // is the above timer running?
|
||||
}
|
||||
|
||||
// NewConverger builds a new converger struct
|
||||
func NewConverger(timeout int, stateFn func(bool) error) *converger {
|
||||
return &converger{
|
||||
// New builds a new converger coordinator.
|
||||
func New(timeout int) *Coordinator {
|
||||
return &Coordinator{
|
||||
timeout: timeout,
|
||||
stateFn: stateFn,
|
||||
channel: make(chan struct{}),
|
||||
control: make(chan bool),
|
||||
lastid: 0,
|
||||
status: make(map[uint64]bool),
|
||||
|
||||
mutex: &sync.RWMutex{},
|
||||
|
||||
//lastid: 0,
|
||||
status: make(map[*UID]struct{}),
|
||||
|
||||
//converged: false, // initial state
|
||||
|
||||
pokeChan: make(chan struct{}, 1), // must be buffered
|
||||
|
||||
readyChan: make(chan struct{}), // ready signal
|
||||
|
||||
//paused: false, // starts off as started
|
||||
pauseSignal: make(chan struct{}),
|
||||
//resumeSignal: make(chan struct{}), // happens on pause
|
||||
//pausedAck: util.NewEasyAck(), // happens on pause
|
||||
|
||||
sendSignal: make(chan bool),
|
||||
|
||||
stateFns: make(map[string]func(bool) error),
|
||||
smutex: &sync.RWMutex{},
|
||||
|
||||
closeChan: make(chan struct{}),
|
||||
wg: &sync.WaitGroup{},
|
||||
}
|
||||
}
|
||||
|
||||
// Register assigns a ConvergerUID to the caller
|
||||
func (obj *converger) Register() ConvergerUID {
|
||||
// Coordinator is the central converger engine.
|
||||
type Coordinator struct {
|
||||
// timeout must be zero (instant) or greater seconds to run. If it's -1
|
||||
// then this is disabled, and we never run stateFns.
|
||||
timeout int
|
||||
|
||||
// mutex is used for controlling access to status and lastid.
|
||||
mutex *sync.RWMutex
|
||||
|
||||
// lastid contains the last uid we used for registration.
|
||||
//lastid uint64
|
||||
// status contains a reference to each active UID.
|
||||
status map[*UID]struct{}
|
||||
|
||||
// converged stores the last convergence state. When this changes, we
|
||||
// run the stateFns.
|
||||
converged bool
|
||||
|
||||
// pokeChan receives a message every time we might need to re-calculate.
|
||||
pokeChan chan struct{}
|
||||
|
||||
// readyChan closes to notify any interested parties that the main loop
|
||||
// is running.
|
||||
readyChan chan struct{}
|
||||
|
||||
// paused represents if this coordinator is paused or not.
|
||||
paused bool
|
||||
// pauseSignal closes to request a pause of this coordinator.
|
||||
pauseSignal chan struct{}
|
||||
// resumeSignal closes to request a resume of this coordinator.
|
||||
resumeSignal chan struct{}
|
||||
// pausedAck is used to send an ack message saying that we've paused.
|
||||
pausedAck *util.EasyAck
|
||||
|
||||
sendSignal chan bool // send pause (false) or resume (true)
|
||||
|
||||
// stateFns run on converged state changes.
|
||||
stateFns map[string]func(bool) error
|
||||
// smutex is used for controlling access to the stateFns map.
|
||||
smutex *sync.RWMutex
|
||||
|
||||
// closeChan closes when we've been requested to shutdown.
|
||||
closeChan chan struct{}
|
||||
// wg waits for everything to finish.
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
// Register creates a new UID which can be used to report converged state. You
|
||||
// must Unregister each UID before Shutdown will be able to finish running.
|
||||
func (obj *Coordinator) Register() *UID {
|
||||
obj.wg.Add(1) // additional tracking for each UID
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
obj.lastid++
|
||||
obj.status[obj.lastid] = false // initialize as not converged
|
||||
return &convergerUID{
|
||||
converger: obj,
|
||||
id: obj.lastid,
|
||||
name: fmt.Sprintf("%d", obj.lastid), // some default
|
||||
timer: nil,
|
||||
running: false,
|
||||
//obj.lastid++
|
||||
uid := &UID{
|
||||
timeout: obj.timeout, // copy the timeout here
|
||||
//id: obj.lastid,
|
||||
//name: fmt.Sprintf("%d", obj.lastid), // some default
|
||||
|
||||
isConverged: &atomic.Bool{},
|
||||
|
||||
poke: obj.poke,
|
||||
|
||||
// timer
|
||||
mutex: &sync.Mutex{},
|
||||
timer: nil,
|
||||
running: false,
|
||||
wg: &sync.WaitGroup{},
|
||||
}
|
||||
uid.unregister = func() { obj.Unregister(uid) } // add unregister func
|
||||
obj.status[uid] = struct{}{} // TODO: add converged state here?
|
||||
return uid
|
||||
}
|
||||
|
||||
// IsConverged gets the converged status of a uid
|
||||
func (obj *converger) IsConverged(uid ConvergerUID) bool {
|
||||
if !uid.IsValid() {
|
||||
panic(fmt.Sprintf("Id of ConvergerUID(%s) is nil!", uid.Name()))
|
||||
}
|
||||
obj.mutex.RLock()
|
||||
isConverged, found := obj.status[uid.ID()] // lookup
|
||||
obj.mutex.RUnlock()
|
||||
if !found {
|
||||
panic("Id of ConvergerUID is unregistered!")
|
||||
}
|
||||
return isConverged
|
||||
}
|
||||
|
||||
// SetConverged updates the converger with the converged state of the UID
|
||||
func (obj *converger) SetConverged(uid ConvergerUID, isConverged bool) error {
|
||||
if !uid.IsValid() {
|
||||
return fmt.Errorf("Id of ConvergerUID(%s) is nil!", uid.Name())
|
||||
}
|
||||
// Unregister removes the UID from the converger coordinator. If you supply an
|
||||
// invalid or unregistered uid to this function, it will panic. An unregistered
|
||||
// UID is no longer part of the convergence checking.
|
||||
func (obj *Coordinator) Unregister(uid *UID) {
|
||||
defer obj.wg.Done() // additional tracking for each UID
|
||||
obj.mutex.Lock()
|
||||
if _, found := obj.status[uid.ID()]; !found {
|
||||
panic("Id of ConvergerUID is unregistered!")
|
||||
defer obj.mutex.Unlock()
|
||||
|
||||
if _, exists := obj.status[uid]; !exists {
|
||||
panic("uid is not registered")
|
||||
}
|
||||
obj.status[uid.ID()] = isConverged // set
|
||||
obj.mutex.Unlock() // unlock *before* poke or deadlock!
|
||||
if isConverged != obj.converged { // only poke if it would be helpful
|
||||
// run in a go routine so that we never block... just queue up!
|
||||
// this allows us to send events, even if we haven't started...
|
||||
go func() { obj.channel <- struct{}{} }()
|
||||
uid.StopTimer() // ignore any errors
|
||||
delete(obj.status, uid)
|
||||
}
|
||||
|
||||
// Run starts the main loop for the converger coordinator. It is commonly run
|
||||
// from a go routine. It blocks until the Shutdown method is run to close it.
|
||||
// NOTE: when we have very short timeouts, if we start before all the resources
|
||||
// have joined the map, then it might appear as if we converged before we did!
|
||||
func (obj *Coordinator) Run(startPaused bool) {
|
||||
obj.wg.Add(1)
|
||||
wg := &sync.WaitGroup{} // needed for the startPaused
|
||||
defer wg.Wait() // don't leave any leftover go routines running
|
||||
if startPaused {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
obj.Pause() // ignore any errors
|
||||
close(obj.readyChan)
|
||||
}()
|
||||
} else {
|
||||
close(obj.readyChan) // we must wait till the wg.Add(1) has happened...
|
||||
}
|
||||
defer obj.wg.Done()
|
||||
for {
|
||||
// pause if one was requested...
|
||||
select {
|
||||
//case <-obj.pauseSignal: // channel closes
|
||||
// obj.pausedAck.Ack() // send ack
|
||||
// // we are paused now, and waiting for resume or exit...
|
||||
// select {
|
||||
// case <-obj.resumeSignal: // channel closes # XXX: RACE READ
|
||||
// // resumed!
|
||||
//
|
||||
// case <-obj.closeChan: // we can always escape
|
||||
// return
|
||||
// }
|
||||
case b, _ := <-obj.sendSignal:
|
||||
if b { // resume
|
||||
panic("unexpected resume") // TODO: continue instead?
|
||||
}
|
||||
// paused
|
||||
obj.pausedAck.Ack() // send ack
|
||||
// we are paused now, and waiting for resume or exit...
|
||||
select {
|
||||
case b, _ := <-obj.sendSignal:
|
||||
if !b { // pause
|
||||
panic("unexpected pause") // TODO: continue instead?
|
||||
}
|
||||
// resumed!
|
||||
|
||||
case <-obj.closeChan: // we can always escape
|
||||
return
|
||||
}
|
||||
|
||||
case _, ok := <-obj.pokeChan: // we got an event (re-calculate)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if err := obj.test(); err != nil {
|
||||
// FIXME: what to do on error ?
|
||||
}
|
||||
|
||||
case <-obj.closeChan: // we can always escape
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ready blocks until the Run loop has started up. This is useful so that we
|
||||
// don't run Shutdown before we've even started up properly.
|
||||
func (obj *Coordinator) Ready() {
|
||||
select {
|
||||
case <-obj.readyChan:
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown sends a signal to the Run loop that it should exit. This blocks
|
||||
// until it does.
|
||||
func (obj *Coordinator) Shutdown() {
|
||||
close(obj.closeChan)
|
||||
obj.wg.Wait()
|
||||
close(obj.pokeChan) // free memory?
|
||||
}
|
||||
|
||||
// Pause pauses the coordinator. It should not be called on an already paused
|
||||
// coordinator. It will block until the coordinator pauses with an
|
||||
// acknowledgment, or until an exit is requested. If the latter happens it will
|
||||
// error. It is NOT thread-safe with the Resume() method so only call either one
|
||||
// at a time.
|
||||
func (obj *Coordinator) Pause() error {
|
||||
if obj.paused {
|
||||
return fmt.Errorf("already paused")
|
||||
}
|
||||
|
||||
obj.pausedAck = util.NewEasyAck()
|
||||
//obj.resumeSignal = make(chan struct{}) // build the resume signal XXX: RACE WRITE
|
||||
//close(obj.pauseSignal)
|
||||
select {
|
||||
case obj.sendSignal <- false:
|
||||
case <-obj.closeChan:
|
||||
return fmt.Errorf("closing")
|
||||
}
|
||||
|
||||
// wait for ack (or exit signal)
|
||||
select {
|
||||
case <-obj.pausedAck.Wait(): // we got it!
|
||||
// we're paused
|
||||
case <-obj.closeChan:
|
||||
return fmt.Errorf("closing")
|
||||
}
|
||||
obj.paused = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isConverged returns true if *every* registered uid has converged
|
||||
func (obj *converger) isConverged() bool {
|
||||
obj.mutex.RLock() // take a read lock
|
||||
defer obj.mutex.RUnlock()
|
||||
for _, v := range obj.status {
|
||||
// Resume unpauses the coordinator. It can be safely called on a brand-new
|
||||
// coordinator that has just started running without incident. It is NOT
|
||||
// thread-safe with the Pause() method, so only call either one at a time.
|
||||
func (obj *Coordinator) Resume() {
|
||||
// TODO: do we need a mutex around Resume?
|
||||
if !obj.paused { // no need to unpause brand-new resources
|
||||
return
|
||||
}
|
||||
|
||||
//obj.pauseSignal = make(chan struct{}) // rebuild for next pause
|
||||
//close(obj.resumeSignal)
|
||||
select {
|
||||
case obj.sendSignal <- true:
|
||||
case <-obj.closeChan:
|
||||
return
|
||||
}
|
||||
|
||||
obj.poke() // unblock and notice the resume if necessary
|
||||
|
||||
obj.paused = false
|
||||
|
||||
// no need to wait for it to resume
|
||||
//return // implied
|
||||
}
|
||||
|
||||
// poke sends a message to the coordinator telling it that it should re-evaluate
|
||||
// whether we're converged or not. This does not block. Do not run this in a
|
||||
// goroutine. It must not be called after Shutdown has been called.
|
||||
func (obj *Coordinator) poke() {
|
||||
// redundant
|
||||
//if len(obj.pokeChan) > 0 {
|
||||
// return
|
||||
//}
|
||||
|
||||
select {
|
||||
case obj.pokeChan <- struct{}{}:
|
||||
default: // if chan is now full because more than one poke happened...
|
||||
}
|
||||
}
|
||||
|
||||
// IsConverged returns true if *every* registered uid has converged. If there
|
||||
// are no registered UID's, then this will return true.
|
||||
func (obj *Coordinator) IsConverged() bool {
|
||||
for _, v := range obj.Status() {
|
||||
if !v { // everyone must be converged for this to be true
|
||||
return false
|
||||
}
|
||||
@@ -155,192 +324,170 @@ func (obj *converger) isConverged() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Unregister dissociates the ConvergedUID from the converged checking
|
||||
func (obj *converger) Unregister(uid ConvergerUID) {
|
||||
if !uid.IsValid() {
|
||||
panic(fmt.Sprintf("Id of ConvergerUID(%s) is nil!", uid.Name()))
|
||||
// test evaluates whether we're converged or not and runs the state change. It
|
||||
// is NOT thread-safe.
|
||||
func (obj *Coordinator) test() error {
|
||||
// TODO: add these checks elsewhere to prevent anything from running?
|
||||
if obj.timeout < 0 {
|
||||
return nil // nothing to do (only run if timeout is valid)
|
||||
}
|
||||
obj.mutex.Lock()
|
||||
uid.StopTimer() // ignore any errors
|
||||
delete(obj.status, uid.ID())
|
||||
obj.mutex.Unlock()
|
||||
uid.InvalidateID()
|
||||
}
|
||||
|
||||
// Start causes a Converger object to start or resume running
|
||||
func (obj *converger) Start() {
|
||||
obj.control <- true
|
||||
}
|
||||
converged := obj.IsConverged()
|
||||
defer func() {
|
||||
obj.converged = converged // set this only at the end...
|
||||
}()
|
||||
|
||||
// Pause causes a Converger object to stop running temporarily
|
||||
func (obj *converger) Pause() { // FIXME: add a sync ACK on pause before return
|
||||
obj.control <- false
|
||||
}
|
||||
|
||||
// Loop is the main loop for a Converger object; it usually runs in a goroutine
|
||||
// TODO: we could eventually have each resource tell us as soon as it converges
|
||||
// and then keep track of the time delays here, to avoid callers needing select
|
||||
// NOTE: when we have very short timeouts, if we start before all the resources
|
||||
// have joined the map, then it might appears as if we converged before we did!
|
||||
func (obj *converger) Loop(startPaused bool) {
|
||||
if obj.control == nil {
|
||||
panic("Converger not initialized correctly")
|
||||
}
|
||||
if startPaused { // start paused without racing
|
||||
select {
|
||||
case e := <-obj.control:
|
||||
if !e {
|
||||
panic("Converger expected true!")
|
||||
}
|
||||
if !converged {
|
||||
if !obj.converged { // were we previously also not converged?
|
||||
return nil // nothing to do
|
||||
}
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case e := <-obj.control: // expecting "false" which means pause!
|
||||
if e {
|
||||
panic("Converger expected false!")
|
||||
}
|
||||
// now i'm paused...
|
||||
select {
|
||||
case e := <-obj.control:
|
||||
if !e {
|
||||
panic("Converger expected true!")
|
||||
}
|
||||
// restart
|
||||
// kick once to refresh the check...
|
||||
go func() { obj.channel <- struct{}{} }()
|
||||
continue
|
||||
}
|
||||
|
||||
case <-obj.channel:
|
||||
if !obj.isConverged() {
|
||||
if obj.converged { // we're doing a state change
|
||||
if obj.stateFn != nil {
|
||||
// call an arbitrary function
|
||||
if err := obj.stateFn(false); err != nil {
|
||||
// FIXME: what to do on error ?
|
||||
}
|
||||
}
|
||||
}
|
||||
obj.converged = false
|
||||
continue
|
||||
}
|
||||
|
||||
// we have converged!
|
||||
if obj.timeout >= 0 { // only run if timeout is valid
|
||||
if !obj.converged { // we're doing a state change
|
||||
if obj.stateFn != nil {
|
||||
// call an arbitrary function
|
||||
if err := obj.stateFn(true); err != nil {
|
||||
// FIXME: what to do on error ?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
obj.converged = true
|
||||
// loop and wait again...
|
||||
}
|
||||
// we're doing a state change
|
||||
// call the arbitrary functions (takes a read lock!)
|
||||
return obj.runStateFns(false)
|
||||
}
|
||||
|
||||
// we have converged!
|
||||
if obj.converged { // were we previously also converged?
|
||||
return nil // nothing to do
|
||||
}
|
||||
|
||||
// call the arbitrary functions (takes a read lock!)
|
||||
return obj.runStateFns(true)
|
||||
}
|
||||
|
||||
// ConvergedTimer adds a timeout to a select call and blocks until then
|
||||
// TODO: this means we could eventually have per resource converged timeouts
|
||||
func (obj *converger) ConvergedTimer(uid ConvergerUID) <-chan time.Time {
|
||||
// be clever: if i'm already converged, this timeout should block which
|
||||
// avoids unnecessary new signals being sent! this avoids fast loops if
|
||||
// we have a low timeout, or in particular a timeout == 0
|
||||
if uid.IsConverged() {
|
||||
// blocks the case statement in select forever!
|
||||
return util.TimeAfterOrBlock(-1)
|
||||
// runStateFns runs the list of stored state functions.
|
||||
func (obj *Coordinator) runStateFns(converged bool) error {
|
||||
obj.smutex.RLock()
|
||||
defer obj.smutex.RUnlock()
|
||||
var keys []string
|
||||
for k := range obj.stateFns {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return util.TimeAfterOrBlock(obj.timeout)
|
||||
sort.Strings(keys)
|
||||
var err error
|
||||
for _, name := range keys { // run in deterministic order
|
||||
fn := obj.stateFns[name]
|
||||
// call an arbitrary function
|
||||
e := fn(converged)
|
||||
err = errwrap.Append(err, e) // list of errors
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// AddStateFn adds a state function to be run on change of converged state.
|
||||
func (obj *Coordinator) AddStateFn(name string, stateFn func(bool) error) error {
|
||||
obj.smutex.Lock()
|
||||
defer obj.smutex.Unlock()
|
||||
if _, exists := obj.stateFns[name]; exists {
|
||||
return fmt.Errorf("a stateFn with that name already exists")
|
||||
}
|
||||
obj.stateFns[name] = stateFn
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveStateFn removes a state function from running on change of converged
|
||||
// state.
|
||||
func (obj *Coordinator) RemoveStateFn(name string) error {
|
||||
obj.smutex.Lock()
|
||||
defer obj.smutex.Unlock()
|
||||
if _, exists := obj.stateFns[name]; !exists {
|
||||
return fmt.Errorf("a stateFn with that name doesn't exist")
|
||||
}
|
||||
delete(obj.stateFns, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Status returns a map of the converged status of each UID.
|
||||
func (obj *converger) Status() map[uint64]bool {
|
||||
status := make(map[uint64]bool)
|
||||
func (obj *Coordinator) Status() map[*UID]bool {
|
||||
status := make(map[*UID]bool)
|
||||
obj.mutex.RLock() // take a read lock
|
||||
defer obj.mutex.RUnlock()
|
||||
for k, v := range obj.status { // make a copy to avoid the mutex
|
||||
status[k] = v
|
||||
for k := range obj.status {
|
||||
status[k] = k.IsConverged()
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
// Timeout returns the timeout in seconds that converger was created with. This
|
||||
// is useful to avoid passing in the timeout value separately when you're
|
||||
// already passing in the Converger struct.
|
||||
func (obj *converger) Timeout() int {
|
||||
// already passing in the Coordinator struct.
|
||||
func (obj *Coordinator) Timeout() int {
|
||||
return obj.timeout
|
||||
}
|
||||
|
||||
// SetStateFn sets the state function to be run on change of converged state.
|
||||
func (obj *converger) SetStateFn(stateFn func(bool) error) {
|
||||
obj.stateFn = stateFn
|
||||
// UID represents one of the probes for the converger coordinator. It is created
|
||||
// by calling the Register method of the Coordinator struct. It should be freed
|
||||
// after use with Unregister.
|
||||
type UID struct {
|
||||
// timeout is a copy of the main timeout. It could eventually be used
|
||||
// for per-UID timeouts too.
|
||||
timeout int
|
||||
// isConverged stores the convergence state of this particular UID.
|
||||
isConverged *atomic.Bool
|
||||
|
||||
// poke stores a reference to the main poke function.
|
||||
poke func()
|
||||
// unregister stores a reference to the unregister function.
|
||||
unregister func()
|
||||
|
||||
// timer
|
||||
mutex *sync.Mutex
|
||||
timer chan struct{}
|
||||
running bool // is the timer running?
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
// Id returns the unique id of this UID object
|
||||
func (obj *convergerUID) ID() uint64 {
|
||||
return obj.id
|
||||
// Unregister removes this UID from the converger coordinator. An unregistered
|
||||
// UID is no longer part of the convergence checking.
|
||||
func (obj *UID) Unregister() {
|
||||
obj.unregister()
|
||||
}
|
||||
|
||||
// Name returns a user defined name for the specific convergerUID.
|
||||
func (obj *convergerUID) Name() string {
|
||||
return obj.name
|
||||
// IsConverged reports whether this UID is converged or not.
|
||||
func (obj *UID) IsConverged() bool {
|
||||
return obj.isConverged.Load()
|
||||
}
|
||||
|
||||
// SetName sets a user defined name for the specific convergerUID.
|
||||
func (obj *convergerUID) SetName(name string) {
|
||||
obj.name = name
|
||||
// SetConverged sets the convergence state of this UID. This is used by the
|
||||
// running timer if one is started. The timer will overwrite any value set by
|
||||
// this method.
|
||||
func (obj *UID) SetConverged(isConverged bool) {
|
||||
obj.isConverged.Store(isConverged)
|
||||
obj.poke() // notify of change
|
||||
}
|
||||
|
||||
// IsValid tells us if the id is valid or has already been destroyed
|
||||
func (obj *convergerUID) IsValid() bool {
|
||||
return obj.id != 0 // an id of 0 is invalid
|
||||
}
|
||||
|
||||
// InvalidateID marks the id as no longer valid
|
||||
func (obj *convergerUID) InvalidateID() {
|
||||
obj.id = 0 // an id of 0 is invalid
|
||||
}
|
||||
|
||||
// IsConverged is a helper function to the regular IsConverged method
|
||||
func (obj *convergerUID) IsConverged() bool {
|
||||
return obj.converger.IsConverged(obj)
|
||||
}
|
||||
|
||||
// SetConverged is a helper function to the regular SetConverged notification
|
||||
func (obj *convergerUID) SetConverged(isConverged bool) error {
|
||||
return obj.converger.SetConverged(obj, isConverged)
|
||||
}
|
||||
|
||||
// Unregister is a helper function to unregister myself
|
||||
func (obj *convergerUID) Unregister() {
|
||||
obj.converger.Unregister(obj)
|
||||
}
|
||||
|
||||
// ConvergedTimer is a helper around the regular ConvergedTimer method
|
||||
func (obj *convergerUID) ConvergedTimer() <-chan time.Time {
|
||||
return obj.converger.ConvergedTimer(obj)
|
||||
}
|
||||
|
||||
// StartTimer runs an invisible timer that automatically converges on timeout.
|
||||
func (obj *convergerUID) StartTimer() (func() error, error) {
|
||||
obj.mutex.Lock()
|
||||
if !obj.running {
|
||||
obj.timer = make(chan struct{})
|
||||
obj.running = true
|
||||
} else {
|
||||
obj.mutex.Unlock()
|
||||
return obj.StopTimer, fmt.Errorf("Timer already started!")
|
||||
// ConvergedTimer adds a timeout to a select call and blocks until then.
|
||||
// TODO: this means we could eventually have per resource converged timeouts
|
||||
func (obj *UID) ConvergedTimer() <-chan time.Time {
|
||||
// be clever: if i'm already converged, this timeout should block which
|
||||
// avoids unnecessary new signals being sent! this avoids fast loops if
|
||||
// we have a low timeout, or in particular a timeout == 0
|
||||
if obj.IsConverged() {
|
||||
// blocks the case statement in select forever!
|
||||
return util.TimeAfterOrBlock(-1)
|
||||
}
|
||||
obj.mutex.Unlock()
|
||||
return util.TimeAfterOrBlock(int(obj.timeout))
|
||||
}
|
||||
|
||||
// StartTimer runs a timer that sets us as converged on timeout. It also returns
|
||||
// a handle to the StopTimer function which should be run before exit.
|
||||
func (obj *UID) StartTimer() (func() error, error) {
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
if obj.running {
|
||||
return obj.StopTimer, fmt.Errorf("timer already started")
|
||||
}
|
||||
obj.timer = make(chan struct{})
|
||||
obj.running = true
|
||||
obj.wg.Add(1)
|
||||
go func() {
|
||||
defer obj.wg.Done()
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-obj.timer: // reset signal channel
|
||||
if !ok { // channel is closed
|
||||
return // false to exit
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
obj.SetConverged(false)
|
||||
|
||||
@@ -348,8 +495,8 @@ func (obj *convergerUID) StartTimer() (func() error, error) {
|
||||
obj.SetConverged(true) // converged!
|
||||
select {
|
||||
case _, ok := <-obj.timer: // reset signal channel
|
||||
if !ok { // channel is closed
|
||||
return // false to exit
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,25 +505,26 @@ func (obj *convergerUID) StartTimer() (func() error, error) {
|
||||
return obj.StopTimer, nil
|
||||
}
|
||||
|
||||
// ResetTimer resets the counter to zero if using a StartTimer internally.
|
||||
func (obj *convergerUID) ResetTimer() error {
|
||||
// ResetTimer resets the timer to zero.
|
||||
func (obj *UID) ResetTimer() error {
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
if obj.running {
|
||||
obj.timer <- struct{}{} // send the reset message
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Timer hasn't been started!")
|
||||
return fmt.Errorf("timer hasn't been started")
|
||||
}
|
||||
|
||||
// StopTimer stops the running timer permanently until a StartTimer is run.
|
||||
func (obj *convergerUID) StopTimer() error {
|
||||
// StopTimer stops the running timer.
|
||||
func (obj *UID) StopTimer() error {
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
if !obj.running {
|
||||
return fmt.Errorf("Timer isn't running!")
|
||||
return fmt.Errorf("timer isn't running")
|
||||
}
|
||||
close(obj.timer)
|
||||
obj.wg.Wait()
|
||||
obj.running = false
|
||||
return nil
|
||||
}
|
||||
|
||||
43
converger/converger_test.go
Normal file
43
converger/converger_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
//go:build !root
|
||||
|
||||
package converger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBufferedChan1(t *testing.T) {
|
||||
ch := make(chan bool, 1)
|
||||
ch <- true
|
||||
close(ch) // closing a channel that's not empty should not block
|
||||
// must be able to exit without blocking anywhere
|
||||
}
|
||||
7
debian/.gitignore
vendored
Normal file
7
debian/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
*.debhelper.log
|
||||
*debhelper
|
||||
changelog
|
||||
debhelper-build-stamp
|
||||
files
|
||||
mgmt.substvars
|
||||
mgmt/*
|
||||
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
||||
9
|
||||
17
debian/control
vendored
Normal file
17
debian/control
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
Source: mgmt
|
||||
Maintainer: Johan Bloemberg (aequitas) <mgmt@ijohan.nl>
|
||||
Build-Depends:
|
||||
debhelper,
|
||||
devscripts,
|
||||
dh-golang,
|
||||
dh-systemd,
|
||||
golang-go,
|
||||
|
||||
Package: mgmt
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, packagekit
|
||||
Suggests: graphviz
|
||||
Description: mgmt: next generation config management!
|
||||
The mgmt tool is a next generation config management solution. It's
|
||||
ready for production, and we hope you try out the future soon. Get
|
||||
involved today!
|
||||
33
debian/copyright
vendored
Normal file
33
debian/copyright
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: mgmt
|
||||
Source: <https://github.com/purpleidea/mgmt>
|
||||
|
||||
Files: *
|
||||
Copyright: Copyright (C) James Shubin and the project contributors
|
||||
License: GPL-3.0
|
||||
|
||||
License: GPL-3.0
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permission under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this program, or any covered work, by linking or combining it
|
||||
with embedded mcl code and modules (and that the embedded mcl code and
|
||||
modules which link with this program, contain a copy of their source code in
|
||||
the authoritative form) containing parts covered by the terms of any other
|
||||
license, the licensors of this program grant you additional permission to
|
||||
convey the resulting work. Furthermore, the licensors of this program grant
|
||||
the original author, James Shubin, additional permission to update this
|
||||
additional permission if he deems it necessary to achieve the goals of this
|
||||
additional permission.
|
||||
11
debian/mgmt.docs
vendored
Normal file
11
debian/mgmt.docs
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
AUTHORS
|
||||
COPYING
|
||||
COPYRIGHT
|
||||
README.md
|
||||
THANKS
|
||||
TODO.md
|
||||
docs
|
||||
examples
|
||||
misc/bashrc.sh
|
||||
misc/delta-cpu.sh
|
||||
misc/mgmt.service
|
||||
2
debian/mgmt.install
vendored
Normal file
2
debian/mgmt.install
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
mgmt usr/bin
|
||||
misc/mgmt.service /lib/systemd/system
|
||||
15
debian/rules
vendored
Executable file
15
debian/rules
vendored
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
export DH_OPTIONS
|
||||
export DH_GOPKG := mgmt
|
||||
export DH_GOLANG_INSTALL_ALL := 1
|
||||
unexport GOROOT
|
||||
|
||||
override_dh_auto_build:
|
||||
make build
|
||||
|
||||
override_dh_auto_test:
|
||||
@echo "Tests are disabled for now"
|
||||
|
||||
%:
|
||||
dh $@ --with=systemd
|
||||
22
doc.go
22
doc.go
@@ -1,19 +1,31 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2016+ James Shubin and the project contributors
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
// Package main provides the main entrypoint for using the `mgmt` software.
|
||||
package main
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
FROM golang:1.6.2
|
||||
FROM golang:1.23
|
||||
|
||||
MAINTAINER Michał Czeraszkiewicz <contact@czerasz.com>
|
||||
|
||||
# Set the reset cache variable
|
||||
# Read more here: http://czerasz.com/2014/11/13/docker-tip-and-tricks/#use-refreshedat-variable-for-better-cache-control
|
||||
ENV REFRESHED_AT 2016-05-10
|
||||
ENV REFRESHED_AT 2020-09-23
|
||||
|
||||
# Update the package list to be able to use required packages
|
||||
RUN apt-get update
|
||||
|
||||
20
docker/Dockerfile-fedora.build
Normal file
20
docker/Dockerfile-fedora.build
Normal file
@@ -0,0 +1,20 @@
|
||||
FROM fedora:41
|
||||
LABEL org.opencontainers.image.authors="laurent.indermuehle@pm.me"
|
||||
|
||||
ENV GOPATH=/root/gopath
|
||||
ENV PATH=/root/gopath/bin:/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/go/bin:/usr/local/bin
|
||||
ENV LD_LIBRARY_PATH=/usr/lib64
|
||||
ENV PKG_CONFIG_PATH=/usr/lib64/pkgconfig
|
||||
|
||||
# This forces make-deps.sh to install Ragel 6.1 instead of 7.0
|
||||
ENV DOCKER=true
|
||||
|
||||
RUN dnf -y install wget unzip git make which gcc gcc-c++ ruby golang
|
||||
|
||||
RUN mkdir -p $GOPATH/src/github.com/purpleidea
|
||||
WORKDIR $GOPATH/src/github.com/purpleidea
|
||||
RUN git clone --recursive https://github.com/purpleidea/mgmt mgmt
|
||||
WORKDIR $GOPATH/src/github.com/purpleidea/mgmt
|
||||
RUN make deps
|
||||
RUN make build
|
||||
CMD ["/bin/bash"]
|
||||
12
docker/Dockerfile.build
Normal file
12
docker/Dockerfile.build
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM centos:7
|
||||
MAINTAINER Karim Boumedhel <karimboumedhel@gmail.com>
|
||||
|
||||
ENV GOPATH=/root/gopath
|
||||
ENV PATH=/opt/rh/rh-ruby22/root/usr/bin:/root/gopath/bin:/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/go/bin
|
||||
ENV LD_LIBRARY_PATH=/opt/rh/rh-ruby22/root/usr/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
|
||||
ENV PKG_CONFIG_PATH=/opt/rh/rh-ruby22/root/usr/lib64/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}
|
||||
|
||||
RUN yum -y install epel-release wget unzip git make which centos-release-scl gcc && sed -i "s/enabled=0/enabled=1/" /etc/yum.repos.d/epel-testing.repo && yum -y install rh-ruby22 && wget -O /opt/go1.23.5.linux-amd64.tar.gz https://storage.googleapis.com/golang/go1.23.5.linux-amd64.tar.gz && tar -C /usr/local -xzf /opt/go1.23.5.linux-amd64.tar.gz
|
||||
RUN mkdir -p $GOPATH/src/github.com/purpleidea && cd $GOPATH/src/github.com/purpleidea && git clone --recursive https://github.com/purpleidea/mgmt
|
||||
RUN go get -u gopkg.in/alecthomas/gometalinter.v1 && cd $GOPATH/src/github.com/purpleidea/mgmt && make deps && make build
|
||||
CMD ["/bin/bash"]
|
||||
@@ -1,10 +1,10 @@
|
||||
FROM golang:1.6.2
|
||||
FROM golang:1.23
|
||||
|
||||
MAINTAINER Michał Czeraszkiewicz <contact@czerasz.com>
|
||||
|
||||
# Set the reset cache variable
|
||||
# Read more here: http://czerasz.com/2014/11/13/docker-tip-and-tricks/#use-refreshedat-variable-for-better-cache-control
|
||||
ENV REFRESHED_AT 2016-05-14
|
||||
ENV REFRESHED_AT 2019-02-06
|
||||
|
||||
RUN apt-get update
|
||||
|
||||
|
||||
9
docker/Dockerfile.static
Normal file
9
docker/Dockerfile.static
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM centos:7
|
||||
MAINTAINER Karim Boumedhel <karimboumedhel@gmail.com>
|
||||
|
||||
RUN yum -y install augeas-libs libvirt-libs && yum clean all
|
||||
ADD mgmt /usr/bin
|
||||
RUN chmod 700 /usr/bin/mgmt
|
||||
|
||||
ENTRYPOINT ["/usr/bin/mgmt"]
|
||||
CMD ["-h"]
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
script_directory="$( cd "$( dirname "$0" )" && pwd )"
|
||||
project_directory=$script_directory/../..
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Stop on any error
|
||||
set -e
|
||||
|
||||
18
docker/scripts/exec-development
Executable file
18
docker/scripts/exec-development
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# runs command provided as argument inside a development (Linux) Docker container
|
||||
|
||||
# Stop on any error
|
||||
set -e
|
||||
|
||||
script_directory="$( cd "$( dirname "$0" )" && pwd )"
|
||||
project_directory=$script_directory/../..
|
||||
|
||||
# Specify the Docker image name
|
||||
image_name='purpleidea/mgmt:development'
|
||||
|
||||
# Run container in development mode
|
||||
docker run --rm --name=mgm_development --user=mgmt \
|
||||
-v "$project_directory:/go/src/github.com/purpleidea/mgmt/" \
|
||||
-w /go/src/github.com/purpleidea/mgmt/ \
|
||||
-it "$image_name" /bin/bash -c "$*"
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Stop on any error
|
||||
set -e
|
||||
|
||||
2
docs/.gitignore
vendored
Normal file
2
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
mgmt-documentation.pdf
|
||||
_build
|
||||
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = mgmt
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
158
docs/conf.py
Normal file
158
docs/conf.py
Normal file
@@ -0,0 +1,158 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# mgmt documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed Feb 15 21:34:09 2017.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
from recommonmark.parser import CommonMarkParser
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
|
||||
source_parsers = {
|
||||
'.md': CommonMarkParser,
|
||||
}
|
||||
|
||||
source_suffix = ['.rst', '.md']
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'mgmt'
|
||||
copyright = u'Copyright (C) James Shubin and the project contributors'
|
||||
author = u'James Shubin'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u''
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u''
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'venv']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
#html_theme = 'alabaster'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'mgmtdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'mgmt.tex', u'mgmt Documentation',
|
||||
u'James Shubin', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'mgmt', u'mgmt Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'mgmt', u'mgmt Documentation',
|
||||
author, 'mgmt', 'Next generation distributed, event-driven, parallel config management!',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
96
docs/contributing.md
Normal file
96
docs/contributing.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Contributing
|
||||
|
||||
What follows is a short guide with information for participants who wish to
|
||||
contribute to the project. It hopes to set both some expectations and boundaries
|
||||
so that we both benefit.
|
||||
|
||||
## Small patches
|
||||
|
||||
If you have a small patch which you believe is straightforward, should be easy
|
||||
to merge, and isn't overly onerous on your time to write, please feel free to
|
||||
send it our way without asking first. Bug fixes are excellent examples of small
|
||||
patches. Please make sure to familiarize yourself with the rough coding style of
|
||||
the project first, and read through the [style guide](style-guide.md).
|
||||
|
||||
## Making an excellent small patch
|
||||
|
||||
As a special case: We'd like to avoid minimal effort, one-off, drive-by patches
|
||||
by bots and contributors looking to increase their "activity" numbers. As an
|
||||
example: a patch which fixes a small linting issue isn't rousing, but a patch
|
||||
that adds a linter test _and_ fixes a small linting issue is, because it shows
|
||||
you put in more effort.
|
||||
|
||||
## Medium patches
|
||||
|
||||
Medium sized patches are especially welcome. Good examples of these patches
|
||||
can include writing a new `mgmt` resource or function. You'll generally need
|
||||
some knowledge of golang interfaces and concurrency to write these patches.
|
||||
Before writing one of these, please make sure you understand some basics about
|
||||
the project and how the tool works. After this, it is recommended that you join
|
||||
our discussion channel to suggest the idea, and ideally include the actual API
|
||||
you'd like to propose before writing the code and sending a patch.
|
||||
|
||||
## Making an excellent medium patch proposal
|
||||
|
||||
The "API" of a resource is the type signature of the resource struct, and the
|
||||
"API" of a function is the type signature or signatures that it supports. (Since
|
||||
functions can be polymorphic, more than one signature can be possible!) A good
|
||||
proposal would likely also comment on the mechanisms the resources or functions
|
||||
would use to watch for events, to check state, and to apply changes. If these
|
||||
mechanisms need new dependencies, a brief survey of which dependencies are
|
||||
available and why you recommend a particular one is encouraged.
|
||||
|
||||
## Large patches or structural and core patches
|
||||
|
||||
Please do not send us large, core or structurally significant patches without
|
||||
first getting our approval and without getting some medium patches in first.
|
||||
These patches take a lot of effort to review, and we don't want to skimp on our
|
||||
commitment to that if we can't muster it. Instead grow our relationship with you
|
||||
on the medium-sized patches first. (A core patch might refer to something that
|
||||
touches either the function engine, resource engine, compiler internals, or
|
||||
something that is part of one of the internal API's.)
|
||||
|
||||
## Expectations and boundaries
|
||||
|
||||
When interacting with the project and soliciting feedback (either for design or
|
||||
during a code review) please keep in mind that the project (unfortunately!) has
|
||||
time constraints and so must prioritize how it handles workloads. If you are
|
||||
someone who has successfully sent in small patches, we will be more willing to
|
||||
spend time mentoring your medium sized patches and so on. Think of it this way:
|
||||
as you show that you're contributing to the project, we'll contribute more to
|
||||
you. Put another way: we can't afford to spend large amounts of time discussing
|
||||
potential patches with you, just to end up nowhere. Build up your reputation
|
||||
with us, and we hope to help grow our symbiosis with you all the while as you
|
||||
grow too!
|
||||
|
||||
## Energy output
|
||||
|
||||
The same goes for users and issue creators. There are times when we simply don't
|
||||
have the cycles to discuss or litigate an issue with you. We wish we did have
|
||||
more time, but it is finite, and running a project is not free. Therefore,
|
||||
please keep in mind that you don't automatically qualify for free support or
|
||||
attention.
|
||||
|
||||
## Attention seeking behaviours
|
||||
|
||||
Some folks spend too much time starting discussions, commenting on issues,
|
||||
"planning" and otherwise displaying attention seeking behaviours. Please avoid
|
||||
doing this as much as possible, especially if you are not already a major
|
||||
contributor to the project. While it may be well intentioned, if it is
|
||||
indistinguishable to us from intentional interference, then it's not welcome
|
||||
behaviour. Remember that Free Software is not free to write. If you require more
|
||||
attention, then either contribute more to the project, or consider paying for a
|
||||
[support contract](https://mgmtconfig.com/).
|
||||
|
||||
## Consulting
|
||||
|
||||
Having said all that, there are some folks who want to do some longer-term
|
||||
planning to decide if our core design and architecture is right for them to
|
||||
invest in. If that's the case, and you aren't already a well-known project
|
||||
contributor, please [contact](https://mgmtconfig.com/) us for a consulting
|
||||
quote. We have packages available for both individuals and businesses.
|
||||
|
||||
## Respect
|
||||
|
||||
Please be mindful and respectful of others when interacting with the project and
|
||||
its contributors. If you cannot abide by that, you may no longer be welcome.
|
||||
163
docs/development.md
Normal file
163
docs/development.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# Development
|
||||
|
||||
This document contains some additional information and help regarding
|
||||
developing `mgmt`. Useful tools, conventions, etc.
|
||||
|
||||
Be sure to read [quick start guide](quick-start-guide.md) first.
|
||||
|
||||
## Vagrant
|
||||
|
||||
If you would like to avoid doing the above steps manually, we have prepared a
|
||||
[Vagrant](https://www.vagrantup.com/) environment for your convenience. From the
|
||||
project directory, run a `vagrant up`, and then a `vagrant status`. From there,
|
||||
you can `vagrant ssh` into the `mgmt` machine. The `MOTD` will explain the rest.
|
||||
This environment isn't commonly used by the `mgmt` developers, so it might not
|
||||
be working properly.
|
||||
|
||||
## Using Docker
|
||||
|
||||
Alternatively, you can check out the [docker folder](../docker/) in order to
|
||||
develop or deploy using docker. This method is not endorsed or supported, so use
|
||||
at your own risk, as it might not be working properly.
|
||||
|
||||
## Information about dependencies
|
||||
|
||||
Software projects have a few different kinds of dependencies. There are _build_
|
||||
dependencies, _runtime_ dependencies, and additionally, a few extra dependencies
|
||||
required for running the _test_ suite.
|
||||
|
||||
### Build
|
||||
|
||||
* A modern `golang` version. The version available in the current Fedora
|
||||
releases is usually supported. This is also distributed as a binary officially
|
||||
by [golang.org](https://golang.org/dl/).
|
||||
|
||||
### Runtime
|
||||
|
||||
A relatively modern GNU/Linux system should be able to run `mgmt` without any
|
||||
problems. Since `mgmt` runs as a single statically compiled binary, all of the
|
||||
library dependencies are included. It is expected, that certain advanced
|
||||
resources require host specific facilities to work. These requirements are
|
||||
listed below:
|
||||
|
||||
| Resource | Dependency | Version | Check version with |
|
||||
|----------|-------------------|-----------------------------|-----------------------------------------------------------|
|
||||
| augeas | augeas-devel | `augeas 1.6` or greater | `dnf info augeas-devel` or `apt-cache show libaugeas-dev` |
|
||||
| file | inotify | `Linux 2.6.27` or greater | `uname -a` |
|
||||
| hostname | systemd-hostnamed | `systemd 25` or greater | `systemctl --version` |
|
||||
| nspawn | systemd-nspawn | `systemd ???` or greater | `systemctl --version` |
|
||||
| pkg | packagekitd | `packagekit 1.x` or greater | `pkcon --version` |
|
||||
| svc | systemd | `systemd ???` or greater | `systemctl --version` |
|
||||
| virt | libvirt-devel | `libvirt 1.2.0` or greater | `dnf info libvirt-devel` or `apt-cache show libvirt-dev` |
|
||||
| virt | libvirtd | `libvirt 1.2.0` or greater | `libvirtd --version` |
|
||||
|
||||
For building a visual representation of the graph, `graphviz` is required.
|
||||
|
||||
To build `mgmt` without augeas support please run:
|
||||
`GOTAGS='noaugeas' make build`
|
||||
|
||||
To build `mgmt` without libvirt support please run:
|
||||
`GOTAGS='novirt' make build`
|
||||
|
||||
To build `mgmt` without docker support please run:
|
||||
`GOTAGS='nodocker' make build`
|
||||
|
||||
To build `mgmt` without augeas, libvirt or docker support please run:
|
||||
`GOTAGS='noaugeas novirt nodocker' make build`
|
||||
|
||||
## OSX/macOS/Darwin development
|
||||
|
||||
Developing and running `mgmt` on macOS is currently not supported (but not
|
||||
discouraged either). Meaning it might work but in the case it doesn't you would
|
||||
have to provide your own patches to fix problems (the project maintainer and
|
||||
community are glad to assist where needed).
|
||||
|
||||
There are currently some issues that make `mgmt` less suitable to run for
|
||||
provisioning macOS. But as a client to provision remote servers it should run
|
||||
fine.
|
||||
|
||||
Since the primary supported systems are Linux and these are the environments
|
||||
tested, it is wise to run these suites during macOS development as well. To ease
|
||||
this, Docker can be leveraged ([Docker for Mac](https://docs.docker.com/docker-for-mac/)).
|
||||
|
||||
Before running any of the commands below create the development Docker image:
|
||||
|
||||
```
|
||||
docker/scripts/build-development
|
||||
```
|
||||
|
||||
This image requires updating every time dependencies (`make-deps.sh`) changes.
|
||||
|
||||
Then to run the test suite:
|
||||
|
||||
```
|
||||
docker run --rm -ti \
|
||||
-v $PWD:/go/src/github.com/purpleidea/mgmt/ \
|
||||
-w /go/src/github.com/purpleidea/mgmt/ \
|
||||
purpleidea/mgmt:development \
|
||||
make test
|
||||
```
|
||||
|
||||
For convenience this command is wrapped in `docker/scripts/exec-development`.
|
||||
|
||||
Basically any command can be executed this way. Because the repository source is
|
||||
mounted into the Docker container invocation will be quick and allow rapid
|
||||
testing, for example:
|
||||
|
||||
```
|
||||
docker/scripts/exec-development test/test-shell.sh load0.sh
|
||||
```
|
||||
|
||||
Other examples:
|
||||
|
||||
```
|
||||
docker/scripts/exec-development make build
|
||||
docker/scripts/exec-development ./mgmt run --tmp-prefix lang examples/lang/load0.mcl
|
||||
```
|
||||
|
||||
Be advised that this method is not supported and it might not be working
|
||||
properly.
|
||||
|
||||
## Testing
|
||||
|
||||
This project has both unit tests in the form of golang tests and integration
|
||||
tests using shell scripting.
|
||||
|
||||
Native golang tests are preferred over tests written in our shell testing
|
||||
framework. Please see [https://golang.org/pkg/testing/](https://golang.org/pkg/testing/)
|
||||
for more information.
|
||||
|
||||
To run all tests:
|
||||
|
||||
```
|
||||
make test
|
||||
```
|
||||
|
||||
There is a library of quick and small integration tests for the language and
|
||||
YAML related things, check out [`test/shell/`](/test/shell). Adding a test is as
|
||||
easy as copying one of the files in [`test/shell/`](/test/shell) and adapting
|
||||
it.
|
||||
|
||||
This test suite won't run by default (unless when on CI server) and needs to be
|
||||
called explicitly using:
|
||||
|
||||
```
|
||||
make test-shell
|
||||
```
|
||||
|
||||
Or run an individual shell test using:
|
||||
|
||||
```
|
||||
make test-shell-load0
|
||||
```
|
||||
|
||||
Tip: you can use TAB completion with `make` to quickly get a list of possible
|
||||
individual tests to run.
|
||||
|
||||
## Tools, integrations, IDE's etc
|
||||
|
||||
### IDE/Editor support
|
||||
|
||||
* Emacs: see `misc/emacs/`
|
||||
* [Textmate](https://github.com/aequitas/mgmt.tmbundle)
|
||||
* [VSCode](https://github.com/aequitas/mgmt.vscode)
|
||||
50
docs/docs.go
Normal file
50
docs/docs.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
// Package docs provides a tool that generates documentation from the source.
|
||||
//
|
||||
// ./mgmt docs generate --output /tmp/docs.json && cat /tmp/docs.json | jq
|
||||
package docs
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// API is the simple interface we expect for any setup items.
|
||||
type API interface {
|
||||
// Main runs everything for this setup item.
|
||||
Main(context.Context) error
|
||||
}
|
||||
|
||||
// Config is a struct of all the configuration values which are shared by all of
|
||||
// the setup utilities. By including this as a separate struct, it can be used
|
||||
// as part of the API if we want.
|
||||
type Config struct {
|
||||
//Foo string `arg:"--foo,env:MGMT_DOCGEN_FOO" help:"Foo..."` // TODO: foo
|
||||
}
|
||||
547
docs/documentation.md
Normal file
547
docs/documentation.md
Normal file
@@ -0,0 +1,547 @@
|
||||
# General documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The `mgmt` tool is a next generation config management solution. It's ready for
|
||||
production, and we hope you try out the future soon. Get involved today!
|
||||
|
||||
## Project Description
|
||||
|
||||
The mgmt tool is a distributed, event driven, config management tool, that
|
||||
supports parallel execution, and librarification to be used as the management
|
||||
foundation in and for, new and existing software.
|
||||
|
||||
For more information, you may like to read some blog posts from the author:
|
||||
|
||||
* [Next generation config mgmt](https://purpleidea.com/blog/2016/01/18/next-generation-configuration-mgmt/)
|
||||
* [Automatic edges in mgmt](https://purpleidea.com/blog/2016/03/14/automatic-edges-in-mgmt/)
|
||||
* [Automatic grouping in mgmt](https://purpleidea.com/blog/2016/03/30/automatic-grouping-in-mgmt/)
|
||||
* [Automatic clustering in mgmt](https://purpleidea.com/blog/2016/06/20/automatic-clustering-in-mgmt/)
|
||||
* [Remote execution in mgmt](https://purpleidea.com/blog/2016/10/07/remote-execution-in-mgmt/)
|
||||
* [Send/Recv in mgmt](https://purpleidea.com/blog/2016/12/07/sendrecv-in-mgmt/)
|
||||
* [Metaparameters in mgmt](https://purpleidea.com/blog/2017/03/01/metaparameters-in-mgmt/)
|
||||
|
||||
There is also an [introductory video](https://www.youtube.com/watch?v=LkEtBVLfygE&html5=1)
|
||||
available. Older videos and other material [is available](on-the-web.md).
|
||||
|
||||
## Setup
|
||||
|
||||
You'll probably want to read the [quick start guide](quick-start-guide.md) to
|
||||
get going.
|
||||
|
||||
## Features
|
||||
|
||||
This section details the numerous features of mgmt and some caveats you might
|
||||
need to be aware of.
|
||||
|
||||
### Autoedges
|
||||
|
||||
Automatic edges, or AutoEdges, is the mechanism in mgmt by which it will
|
||||
automatically create dependencies for you between resources. For example,
|
||||
since mgmt can discover which files are installed by a package it will
|
||||
automatically ensure that any file resource you declare that matches a
|
||||
file installed by your package resource will only be processed after the
|
||||
package is installed.
|
||||
|
||||
#### Controlling autoedges
|
||||
|
||||
Though autoedges is likely to be very helpful and avoid you having to declare
|
||||
all dependencies explicitly, there are cases where this behaviour is
|
||||
undesirable.
|
||||
|
||||
Some distributions allow package installations to automatically start the
|
||||
service they ship. This can be problematic in the case of packages like MySQL
|
||||
as there are configuration options that need to be set before MySQL is ever
|
||||
started for the first time (or you'll need to wipe the data directory). In
|
||||
order to handle this situation you can disable autoedges per resource and
|
||||
explicitly declare that you want `my.cnf` to be written to disk before the
|
||||
installation of the `mysql-server` package.
|
||||
|
||||
You can disable autoedges for a resource by setting the `autoedge` key on
|
||||
the meta attributes of that resource to `false`.
|
||||
|
||||
#### Blog post
|
||||
|
||||
You can read the introductory blog post about this topic here:
|
||||
[https://purpleidea.com/blog/2016/03/14/automatic-edges-in-mgmt/](https://purpleidea.com/blog/2016/03/14/automatic-edges-in-mgmt/)
|
||||
|
||||
### Autogrouping
|
||||
|
||||
Automatic grouping or AutoGroup is the mechanism in mgmt by which it will
|
||||
automatically group multiple resource vertices into a single one. This is
|
||||
particularly useful for grouping multiple package resources into a single
|
||||
resource, since the multiple installations can happen together in a single
|
||||
transaction, which saves a lot of time because package resources typically have
|
||||
a large fixed cost to running (downloading and verifying the package repo) and
|
||||
if they are grouped they share this fixed cost. This grouping feature can be
|
||||
used for other use cases too.
|
||||
|
||||
You can disable autogrouping for a resource by setting the `autogroup` key on
|
||||
the meta attributes of that resource to `false`.
|
||||
|
||||
#### Blog post
|
||||
|
||||
You can read the introductory blog post about this topic here:
|
||||
[https://purpleidea.com/blog/2016/03/30/automatic-grouping-in-mgmt/](https://purpleidea.com/blog/2016/03/30/automatic-grouping-in-mgmt/)
|
||||
|
||||
### Automatic clustering
|
||||
|
||||
Automatic clustering is a feature by which mgmt automatically builds, scales,
|
||||
and manages the embedded etcd cluster which is compiled into mgmt itself. It is
|
||||
quite helpful for rapidly bootstrapping clusters and avoiding the extra work to
|
||||
setup etcd.
|
||||
|
||||
If you prefer to avoid this feature. you can always opt to use an existing etcd
|
||||
cluster that is managed separately from mgmt by pointing your mgmt agents at it
|
||||
with the `--seeds` variable.
|
||||
|
||||
#### Blog post
|
||||
|
||||
You can read the introductory blog post about this topic here:
|
||||
[https://purpleidea.com/blog/2016/06/20/automatic-clustering-in-mgmt/](https://purpleidea.com/blog/2016/06/20/automatic-clustering-in-mgmt/)
|
||||
|
||||
### Remote ("agent-less") mode
|
||||
|
||||
Remote mode is a special mode that lets you kick off mgmt runs on one or more
|
||||
remote machines which are only accessible via SSH. In this mode the initiating
|
||||
host connects over SSH, copies over the `mgmt` binary, opens an SSH tunnel, and
|
||||
runs the remote program while simultaneously passing the etcd traffic back
|
||||
through the tunnel so that the initiators etcd cluster can be used to exchange
|
||||
resource data.
|
||||
|
||||
The interesting benefit of this architecture is that multiple hosts which can't
|
||||
connect directly use the initiator to pass the important traffic through to each
|
||||
other. Once the cluster has converged all the remote programs can shutdown
|
||||
leaving no residual agent.
|
||||
|
||||
This mode can also be useful for bootstrapping a new host where you'd like to
|
||||
have the service run continuously and as part of an mgmt cluster normally.
|
||||
|
||||
In particular, when combined with the `--converged-timeout` parameter, the
|
||||
entire set of running mgmt agents will need to all simultaneously converge for
|
||||
the group to exit. This is particularly useful for bootstrapping new clusters
|
||||
which need to exchange information that is only available at run time.
|
||||
|
||||
This existed in earlier versions of mgmt as a `--remote` option, but it has been
|
||||
removed and is being ported to a more powerful variant where you can remote
|
||||
execute via a `remote` resource.
|
||||
|
||||
#### Blog post
|
||||
|
||||
You can read the introductory blog post about this topic here:
|
||||
[https://purpleidea.com/blog/2016/10/07/remote-execution-in-mgmt/](https://purpleidea.com/blog/2016/10/07/remote-execution-in-mgmt/)
|
||||
|
||||
### Puppet support
|
||||
|
||||
You can supply a puppet manifest instead of creating the (YAML) graph manually.
|
||||
Puppet must be installed and in `mgmt`'s search path. You also need the
|
||||
[ffrank-mgmtgraph puppet module](https://forge.puppet.com/ffrank/mgmtgraph).
|
||||
|
||||
Invoke `mgmt` with the `--puppet` switch, which supports 3 variants:
|
||||
|
||||
1. Request the configuration from the puppet server (like `puppet agent` does)
|
||||
|
||||
`mgmt run puppet --puppet agent`
|
||||
|
||||
2. Compile a local manifest file (like `puppet apply`)
|
||||
|
||||
`mgmt run puppet --puppet /path/to/my/manifest.pp`
|
||||
|
||||
3. Compile an ad hoc manifest from the commandline (like `puppet apply -e`)
|
||||
|
||||
`mgmt run puppet --puppet 'file { "/etc/ntp.conf": ensure => file }'`
|
||||
|
||||
For more details and caveats see [puppet-guide.md](puppet-guide.md).
|
||||
|
||||
#### Blog post
|
||||
|
||||
An introductory post on the puppet support is on
|
||||
[Felix's blog](http://ffrank.github.io/features/2016/06/19/puppet-powered-mgmt/).
|
||||
|
||||
## Reference
|
||||
|
||||
Please note that there are a number of undocumented options. For more
|
||||
information on these options, please view the source at:
|
||||
[https://github.com/purpleidea/mgmt/](https://github.com/purpleidea/mgmt/).
|
||||
If you feel that a well used option needs documenting here, please patch it!
|
||||
|
||||
### Overview of reference
|
||||
|
||||
* [Meta parameters](#meta-parameters): List of available resource meta parameters.
|
||||
* [Lang metadata file](#lang-metadata-file): Lang metadata file format.
|
||||
* [Graph definition file](#graph-definition-file): Main graph definition file.
|
||||
* [Command line](#command-line): Command line parameters.
|
||||
* [Compilation options](#compilation-options): Compilation options.
|
||||
|
||||
### Meta parameters
|
||||
|
||||
These meta parameters are special parameters (or properties) which can apply to
|
||||
any resource. The usefulness of doing so will depend on the particular meta
|
||||
parameter and resource combination.
|
||||
|
||||
#### AutoEdge
|
||||
|
||||
Boolean. Should we generate auto edges for this resource?
|
||||
|
||||
#### AutoGroup
|
||||
|
||||
Boolean. Should we attempt to automatically group this resource with others?
|
||||
|
||||
#### Noop
|
||||
|
||||
Boolean. Should the Apply portion of the CheckApply method of the resource
|
||||
make any changes? Noop is a concatenation of no-operation.
|
||||
|
||||
#### Retry
|
||||
|
||||
Integer. The number of times to retry running the resource on error. Use -1 for
|
||||
infinite. This currently applies for both the Watch operation (which can fail)
|
||||
and for the CheckApply operation. While they could have separate values, I've
|
||||
decided to use the same ones for both until there's a proper reason to want to
|
||||
do something differently for the Watch errors.
|
||||
|
||||
#### Delay
|
||||
|
||||
Integer. Number of milliseconds to wait between retries. The same value is
|
||||
shared between the Watch and CheckApply retries. This currently applies for both
|
||||
the Watch operation (which can fail) and for the CheckApply operation. While
|
||||
they could have separate values, I've decided to use the same ones for both
|
||||
until there's a proper reason to want to do something differently for the Watch
|
||||
errors.
|
||||
|
||||
#### Poll
|
||||
|
||||
Integer. Number of seconds to wait between `CheckApply` checks. If this is
|
||||
greater than zero, then the standard event based `Watch` mechanism for this
|
||||
resource is replaced with a simple polling mechanism. In general, this is not
|
||||
recommended, unless you have a very good reason for doing so.
|
||||
|
||||
Please keep in mind that if you have a resource which changes every `I` seconds,
|
||||
and you poll it every `J` seconds, and you've asked for a converged timeout of
|
||||
`K` seconds, and `I <= J <= K`, then your graph will likely never converge.
|
||||
|
||||
When polling, the system detects that a resource is not converged if its
|
||||
`CheckApply` method returns false. This allows a resource which changes every
|
||||
`I` seconds, and which is polled every `J` seconds, and with a converged timeout
|
||||
of `K` seconds to still converge when `J <= K`, as long as `I > J || I > K`,
|
||||
which is another way of saying that if the resource finally settles down to give
|
||||
the graph enough time, it can probably converge.
|
||||
|
||||
#### Limit
|
||||
|
||||
Float. Maximum rate of `CheckApply` runs started per second. Useful to limit
|
||||
an especially _eventful_ process from causing excessive checks to run. This
|
||||
defaults to `+Infinity` which adds no limiting. If you change this value, you
|
||||
will also need to change the `Burst` value to a non-zero value. Please see the
|
||||
[rate](https://godoc.org/golang.org/x/time/rate) package for more information.
|
||||
|
||||
#### Burst
|
||||
|
||||
Integer. Burst is the maximum number of runs which can happen without invoking
|
||||
the rate limiter as designated by the `Limit` value. If the `Limit` is not set
|
||||
to `+Infinity`, this must be a non-zero value. Please see the
|
||||
[rate](https://godoc.org/golang.org/x/time/rate) package for more information.
|
||||
|
||||
#### Sema
|
||||
|
||||
List of string ids. Sema is a P/V style counting semaphore which can be used to
|
||||
limit parallelism during the CheckApply phase of resource execution. Each
|
||||
resource can have `N` different semaphores which share a graph global namespace.
|
||||
Each semaphore has a maximum count associated with it. The default value of the
|
||||
size is 1 (one) if size is unspecified. Each string id is the unique id of the
|
||||
semaphore. If the id contains a trailing colon (:) followed by a positive
|
||||
integer, then that value is the max size for that semaphore. Valid semaphore
|
||||
id's include: `some_id`, `hello:42`, `not:smart:4` and `:13`. It is expected
|
||||
that the last bare example be only used by the engine to add a global semaphore.
|
||||
|
||||
#### Rewatch
|
||||
|
||||
Boolean. Rewatch specifies whether we re-run the Watch worker during a graph
|
||||
swap if it has errored. When doing a graph compare to swap the graphs, if this
|
||||
is true, and this particular worker has errored, then we'll remove it and add it
|
||||
back as a new vertex, thus causing it to run again. This is different from the
|
||||
`Retry` metaparam which applies during the normal execution. It is only when
|
||||
this is exhausted that we're in permanent worker failure, and only then can we
|
||||
rely on this metaparam.
|
||||
|
||||
#### Realize
|
||||
|
||||
Boolean. Realize ensures that the resource is guaranteed to converge at least
|
||||
once before a potential graph swap removes or changes it. This guarantee is
|
||||
useful for fast changing graphs, to ensure that the brief creation of a resource
|
||||
is seen. This guarantee does not prevent against the engine quitting normally,
|
||||
and it can't guarantee it if the resource is blocked because of a failed
|
||||
pre-requisite resource.
|
||||
*XXX: This is currently not implemented!*
|
||||
|
||||
#### Dollar
|
||||
|
||||
Boolean. Dollar allows you to have a resource name that starts with a `$` sign.
|
||||
This is false by default. This helps you catch cases when you write code like:
|
||||
|
||||
```mcl
|
||||
$foo = "/tmp/file1"
|
||||
file "$foo" {} # incorrect!
|
||||
```
|
||||
|
||||
The above code would ignore the `$foo` variable and attempt to make a file named
|
||||
`$foo` which would obviously not work. To correctly interpolate a variable, you
|
||||
need to surround the name with curly braces.
|
||||
|
||||
```mcl
|
||||
$foo = "/tmp/file1"
|
||||
file "${foo}" {} # correct!
|
||||
```
|
||||
|
||||
This meta param is a safety measure to make your life easier. It works for all
|
||||
resources. If someone comes up with a resource which would routinely start with
|
||||
a dollar sign, then we can revisit the default for this resource kind.
|
||||
|
||||
#### Hidden
|
||||
|
||||
Boolean. Hidden means that this resource will not get executed on the resource
|
||||
graph on which it is defined. This can be used as a simple boolean switch, or,
|
||||
more commonly in combination with the Export meta param which specifies that the
|
||||
resource params are exported into the shared database. When this is true, it
|
||||
does not prevent export. In fact, it is commonly used in combination with
|
||||
Export. Using this option will still include it in the resource graph, but it
|
||||
will exist there in a special "mode" where it will not conflict with any other
|
||||
identically named resources. It can even be used as part of an edge or via a
|
||||
send/recv receiver. It can NOT be a sending vertex. These properties
|
||||
differentiate the use of this instead of simply wrapping a resource in an "if"
|
||||
statement.
|
||||
|
||||
#### Export
|
||||
|
||||
List of strings. Export is a list of hostnames (and/or the special "*" entry)
|
||||
which if set, will mark this resource data as intended for export to those
|
||||
hosts. This does not prevent any users of the shared data storage from reading
|
||||
these values, so if you want to guarantee secrecy, use the encryption
|
||||
primitives. This only labels the data accordingly, so that other hosts can know
|
||||
what data is available for them to collect. The (kind, name, host) export triple
|
||||
must be unique from any given exporter. In other words, you may not export two
|
||||
different instances of a kind+name to the same host, the exports must not
|
||||
conflict. On resource collect, this parameter is not preserved.
|
||||
|
||||
```mcl
|
||||
file "/tmp/foo" {
|
||||
state => "exists",
|
||||
content => "i'm exported!\n",
|
||||
|
||||
Meta:hidden => true,
|
||||
Meta:export => ["h1",],
|
||||
}
|
||||
|
||||
file "/tmp/foo" {
|
||||
state => "exists",
|
||||
content => "i'm exported AND i'm used here\n",
|
||||
|
||||
Meta:export => ["h1",],
|
||||
}
|
||||
```
|
||||
|
||||
#### Reverse
|
||||
|
||||
Boolean. Reverse is a property that some resources can implement that specifies
|
||||
that some "reverse" operation should happen when that resource "disappears". A
|
||||
disappearance happens when a resource is defined in one instance of the graph,
|
||||
and is gone in the subsequent one. This disappearance can happen if it was
|
||||
previously in an if statement that then becomes false.
|
||||
|
||||
This is helpful for building robust programs with the engine. The engine adds a
|
||||
"reversed" resource to that subsequent graph to accomplish the desired "reverse"
|
||||
mechanics. The specifics of what this entails is a property of the particular
|
||||
resource that is being "reversed".
|
||||
|
||||
It might be wise to combine the use of this meta parameter with the use of the
|
||||
`realize` meta parameter to ensure that your reversed resource actually runs at
|
||||
least once, if there's a chance that it might be gone for a while.
|
||||
|
||||
### Lang metadata file
|
||||
|
||||
Any module *must* have a metadata file in its root. It must be named
|
||||
`metadata.yaml`, even if it's empty. You can specify zero or more values in yaml
|
||||
format which can change how your module behaves, and where the `mcl` language
|
||||
looks for code and other files. The most important top level keys are: `main`,
|
||||
`path`, `files`, and `license`.
|
||||
|
||||
#### Main
|
||||
|
||||
The `main` key points to the default entry point of your code. It must be a
|
||||
relative path if specified. If it's empty it defaults to `main.mcl`. It should
|
||||
generally not be changed. It is sometimes set to `main/main.mcl` if you'd like
|
||||
your modules code out of the root and into a child directory for cases where you
|
||||
don't plan on having a lot deeper imports relative to `main.mcl` and all those
|
||||
files would clutter things up.
|
||||
|
||||
#### Path
|
||||
|
||||
The `path` key specifies the modules import search directory to use for this
|
||||
module. You can specify this if you'd like to vendor something for your module.
|
||||
In general, if you use it, please use the convention: `path/`. If it's not
|
||||
specified, you will default to the parent modules directory.
|
||||
|
||||
#### Files
|
||||
|
||||
The `files` key specifies some additional files that will get included in your
|
||||
deploy. It defaults to `files/`.
|
||||
|
||||
#### License
|
||||
|
||||
The `license` key allows you to specify a license for the module. Please specify
|
||||
one so that everyone can enjoy your code! Use a "short license identifier", like
|
||||
`LGPLv3+`, or `MIT`. The former is a safe choice if you're not sure what to use.
|
||||
|
||||
### Graph definition file
|
||||
|
||||
graph.yaml is the compiled graph definition file. The format is currently
|
||||
undocumented, but by looking through the [examples/](https://github.com/purpleidea/mgmt/tree/master/examples/yaml/)
|
||||
you can probably figure out most of it, as it's fairly intuitive. It's not
|
||||
recommended that you use this, since it's preferable to write code in the
|
||||
[mcl language](language-guide.md) front-end.
|
||||
|
||||
### Command line
|
||||
|
||||
The main interface to the `mgmt` tool is the command line. For the most recent
|
||||
documentation, please run `mgmt --help`.
|
||||
|
||||
#### `--converged-timeout <seconds>`
|
||||
|
||||
Exit if the machine has converged for approximately this many seconds.
|
||||
|
||||
#### `--max-runtime <seconds>`
|
||||
|
||||
Exit when the agent has run for approximately this many seconds. This is not
|
||||
generally recommended, but may be useful for users who know what they're doing.
|
||||
|
||||
#### `--noop`
|
||||
|
||||
Globally force all resources into no-op mode. This also disables the export to
|
||||
etcd functionality, but does not disable resource collection, however all
|
||||
resources that are collected will have their individual noop settings set.
|
||||
|
||||
#### `--sema <size>`
|
||||
|
||||
Globally add a counting semaphore of this size to each resource in the graph.
|
||||
The semaphore will get given an id of `:size`. In other words if you specify a
|
||||
size of 42, you can expect a semaphore if named: `:42`. It is expected that
|
||||
consumers of the semaphore metaparameter always include a prefix to avoid a
|
||||
collision with this globally defined semaphore. The size value must be greater
|
||||
than zero at this time. The traditional non-parallel execution found in config
|
||||
management tools such as `puppet` can be obtained with `--sema 1`.
|
||||
|
||||
#### `--ssh-priv-id-rsa`
|
||||
|
||||
Specify the path for finding SSH keys. This defaults to `~/.ssh/id_rsa`. To
|
||||
never use this method of authentication, set this to the empty string.
|
||||
|
||||
#### `--prefix <path>`
|
||||
|
||||
Specify a path to a custom working directory prefix. This directory will get
|
||||
created if it does not exist. This usually defaults to `/var/lib/mgmt/`. This
|
||||
can't be combined with the `--tmp-prefix` option. It can be combined with the
|
||||
`--allow-tmp-prefix` option.
|
||||
|
||||
#### `--tmp-prefix`
|
||||
|
||||
If this option is specified, a temporary prefix will be used instead of the
|
||||
default prefix. This can't be combined with the `--prefix` option.
|
||||
|
||||
#### `--allow-tmp-prefix`
|
||||
|
||||
If this option is specified, we will attempt to fall back to a temporary prefix
|
||||
if the primary prefix couldn't be created. This is useful for avoiding failures
|
||||
in environments where the primary prefix may or may not be available, but you'd
|
||||
like to try. The canonical example is when running `mgmt` with remote execution
|
||||
there might be a cached copy of the binary in the primary prefix, but if there's
|
||||
no binary available continue working in a temporary directory to avoid failure.
|
||||
|
||||
### Compilation options
|
||||
|
||||
You can control some compilation variables by using environment variables.
|
||||
|
||||
#### Disable libvirt support
|
||||
|
||||
If you wish to compile mgmt without libvirt, you can use the following command:
|
||||
|
||||
```
|
||||
GOTAGS=novirt make build
|
||||
```
|
||||
|
||||
#### Disable augeas support
|
||||
|
||||
If you wish to compile mgmt without augeas support, you can use the following
|
||||
command:
|
||||
|
||||
```
|
||||
GOTAGS=noaugeas make build
|
||||
```
|
||||
|
||||
#### Disable docker support
|
||||
|
||||
If you wish to compile mgmt without docker support, you can use the following
|
||||
command:
|
||||
|
||||
```
|
||||
GOTAGS=nodocker make build
|
||||
```
|
||||
|
||||
#### Combining compile-time flags
|
||||
|
||||
You can combine multiple tags by using a space-separated list:
|
||||
|
||||
```
|
||||
GOTAGS="noaugeas novirt nodocker" make build
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
For example configurations, please consult the [examples/](https://github.com/purpleidea/mgmt/tree/master/examples)
|
||||
directory in the git source repository. It is available from:
|
||||
|
||||
[https://github.com/purpleidea/mgmt/tree/master/examples](https://github.com/purpleidea/mgmt/tree/master/examples)
|
||||
|
||||
### Systemd:
|
||||
|
||||
See [`misc/mgmt.service`](../misc/mgmt.service) for a sample systemd unit file.
|
||||
This unit file is part of the RPM.
|
||||
|
||||
To specify your custom options for `mgmt` on a systemd distro:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /etc/systemd/system/mgmt.service.d/
|
||||
|
||||
cat > /etc/systemd/system/mgmt.service.d/env.conf <<EOF
|
||||
# Environment variables:
|
||||
MGMT_SEEDS=http://127.0.0.1:2379
|
||||
MGMT_CONVERGED_TIMEOUT=-1
|
||||
MGMT_MAX_RUNTIME=0
|
||||
|
||||
# Other CLI options if necessary.
|
||||
#OPTS="--max-runtime=0"
|
||||
EOF
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
This is a project that I started in my free time in 2013. Development is driven
|
||||
by all of our collective patches! Dive right in, and start hacking!
|
||||
Please contact me if you'd like to invite me to speak about this at your event.
|
||||
|
||||
You can follow along [on my technical blog](https://purpleidea.com/blog/).
|
||||
|
||||
To report any bugs, please file a ticket at: [https://github.com/purpleidea/mgmt/issues](https://github.com/purpleidea/mgmt/issues).
|
||||
|
||||
## Authors
|
||||
|
||||
Copyright (C) James Shubin and the project contributors
|
||||
|
||||
Please see the
|
||||
[AUTHORS](https://github.com/purpleidea/mgmt/tree/master/AUTHORS) file
|
||||
for more information.
|
||||
|
||||
* [github](https://github.com/purpleidea/)
|
||||
* [@purpleidea](https://twitter.com/#!/purpleidea)
|
||||
* [https://purpleidea.com/](https://purpleidea.com/)
|
||||
537
docs/faq.md
Normal file
537
docs/faq.md
Normal file
@@ -0,0 +1,537 @@
|
||||
## Frequently asked questions
|
||||
|
||||
(Send your questions as a patch to this FAQ! I'll review it, merge it, and
|
||||
respond by commit with the answer.)
|
||||
|
||||
### Why did you start this project?
|
||||
|
||||
I wanted a next generation config management solution that didn't have all of
|
||||
the design flaws or limitations that the current generation of tools do, and no
|
||||
tool existed!
|
||||
|
||||
### Why did you choose `golang` for the project?
|
||||
|
||||
When I started working on the project, I needed to choose a language that
|
||||
already had an implementation of a distributed consensus algorithm available.
|
||||
That meant [Paxos](https://en.wikipedia.org/wiki/Paxos_(computer_science)) or
|
||||
[Raft](https://en.wikipedia.org/wiki/Raft_(computer_science)). Golang was one
|
||||
language that actually had two different Raft implementations, `etcd`, and
|
||||
`consul`. Other design requirements included something that was reasonably fast,
|
||||
typed and memory-safe, and suited for systems engineering. After a reasonably
|
||||
extensive search, I chose `golang`. I think it was the right decision. There are
|
||||
a number of other features of the language which helped influence the decision.
|
||||
|
||||
### How do I contribute to the project if I don't know `golang`?
|
||||
|
||||
There are many different ways you can contribute to the project. They can be
|
||||
broadly divided into two main categories:
|
||||
|
||||
1. With contributions written in `golang`
|
||||
2. With contributions _not_ written in `golang`
|
||||
|
||||
If you do not know `golang`, and have no desire to learn, you can still
|
||||
contribute to mgmt by using it, testing it, writing docs, or even just by
|
||||
telling your friends about it. If you don't mind some coding, learning about the
|
||||
[mgmt language](https://purpleidea.com/blog/2018/02/05/mgmt-configuration-language/)
|
||||
might be an enjoyable experience for you. It is a small [DSL](https://en.wikipedia.org/wiki/Domain-specific_language)
|
||||
and not a general purpose programming language, and you might find it more fun
|
||||
than what you're typically used to. One of the reasons the mgmt author got into
|
||||
writing automation modules, was because he found it much more fun to build with
|
||||
a higher level DSL, than in a general purpose programming language.
|
||||
|
||||
If you do not know `golang`, and would like to learn, are a beginner and want to
|
||||
improve your skills, or want to gain some great interdisciplinary systems
|
||||
engineering knowledge around a cool automation project, we're happy to mentor
|
||||
you. Here are some pre-requisites steps which we recommend:
|
||||
|
||||
1. Make sure you have a somewhat recent GNU/Linux environment to hack on. A
|
||||
recent [Fedora](https://getfedora.org/) or [Debian](https://www.debian.org/)
|
||||
environment is recommended. Developing, testing, and contributing on `macOS` or
|
||||
`Windows` will be either more difficult or impossible.
|
||||
2. Ensure that you're mildly comfortable with the basics of using `git`. You can
|
||||
find a number of tutorials online.
|
||||
3. Spend between four to six hours with the [golang tour](https://tour.golang.org/).
|
||||
Skip over the longer problems, but try and get a solid overview of everything.
|
||||
If you forget something, you can always go back and repeat those parts.
|
||||
4. Connect to our [#mgmtconfig](https://matrix.to/#/#mgmtconfig:matrix.org)
|
||||
Matrix channel and hang out with us there out there.
|
||||
5. Now it's time to try and starting writing a patch! We have tagged a bunch of
|
||||
[open issues as #mgmtlove](https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%3Amgmtlove)
|
||||
for new users to have somewhere to get involved. Look through them to see if
|
||||
something interests you. If you find one, let us know you're working on it by
|
||||
leaving a comment in the ticket. We'll be around to answer questions in the
|
||||
channel, and to create new issues if there wasn't something that fit your
|
||||
interests. When you submit a patch, we'll review it and give you some feedback.
|
||||
Over time, we hope you'll learn a lot while supporting the project! Now get
|
||||
hacking!
|
||||
|
||||
### Is this project ready for production?
|
||||
|
||||
It's getting pretty close. I'm able to write modules for it now!
|
||||
|
||||
Compared to some existing automation tools out there, mgmt is a relatively new
|
||||
project. It is probably not as feature complete as some other software, but it
|
||||
also offers a number of features which are not currently available elsewhere.
|
||||
|
||||
Because we have not released a `1.0` release yet, we are not guaranteeing
|
||||
stability of the internal or external API's. We only change them if it's really
|
||||
necessary, and we don't expect anything particularly drastic to occur. We would
|
||||
expect it to be relatively easy to adapt your code if such changes happened.
|
||||
|
||||
As with all software, bugs can occur, and while we make no guarantees of being
|
||||
bug-free, there are a number of things we've done to reduce the chances of one
|
||||
causing you trouble:
|
||||
|
||||
1. Our software is written in golang, which is a memory-safe language, and which
|
||||
is known to reduce or eliminate entire classes of bugs.
|
||||
2. We have a test suite which we run on every commit, and every 24 hours. If you
|
||||
have a particular case that you'd like to test, you are welcome to add it in!
|
||||
3. The mgmt language itself offers a number of safety features. You can
|
||||
[read about them in the introductory blog post](https://purpleidea.com/blog/2018/02/05/mgmt-configuration-language/).
|
||||
|
||||
Having said all this, as with all software, there are still missing features
|
||||
which some users might want in their production environments. We're working hard
|
||||
to get all of those implemented, but we hope that you'll get involved and help
|
||||
us finish off the ones that are most important to you. We are happy to mentor
|
||||
new contributors, and have even [tagged](https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%3Amgmtlove)
|
||||
a number of issues if you need help getting started.
|
||||
|
||||
Some of the current limitations include:
|
||||
|
||||
* Auth hasn't been implemented yet, so you should only use it in trusted
|
||||
environments (not on publicly accessible networks) for now.
|
||||
* The number of built-in core functions is still small. You may encounter
|
||||
scenarios where you're missing a function. The good news is that it's relatively
|
||||
easy to add this missing functionality yourself. In time, with your help, the
|
||||
list will grow!
|
||||
* Large file distribution is not yet implemented. You might want a scenario
|
||||
where mgmt is used to distribute large files (such as `.iso` images) throughout
|
||||
your cluster. While this isn't a common use-case, it won't be possible until
|
||||
someone wants to write the patch. (Mentoring available!) You can workaround this
|
||||
easily by storing those files on a separate fileserver for the interim.
|
||||
* There isn't an ecosystem of community `modules` yet. We've got this on our
|
||||
roadmap, so please stay tuned!
|
||||
|
||||
We hope you'll participate as an early adopter. Every additional pair of helping
|
||||
hands gets us all there faster! It's quite possible to use this to build useful
|
||||
automation today, and we hope you'll start getting familiar with the software.
|
||||
|
||||
### Why did you use etcd? What about consul?
|
||||
|
||||
Etcd and consul are both written in golang, which made them the top two
|
||||
contenders for my prototype. Ultimately a choice had to be made, and etcd was
|
||||
chosen, but it was also somewhat arbitrary. If there is available interest,
|
||||
good reasoning, *and* patches, then we would consider either switching or
|
||||
supporting both, but this is not a high priority at this time.
|
||||
|
||||
### Can I use an existing etcd cluster instead of the automatic embedded servers?
|
||||
|
||||
Yes, it's possible to use an existing etcd cluster instead of the automatic,
|
||||
elastic embedded etcd servers. To do so, simply point to the cluster with the
|
||||
`--seeds` variable, the same way you would if you were seeding a new member to
|
||||
an existing mgmt cluster.
|
||||
|
||||
The downside to this approach is that you won't benefit from the automatic
|
||||
elastic nature of the embedded etcd servers, and that you're responsible if you
|
||||
accidentally break your etcd cluster, or if you use an unsupported version.
|
||||
|
||||
### In `mgmt` you talk about events. What is this referring to?
|
||||
|
||||
Mgmt has two main concepts that involve "events":
|
||||
1. Events in the [resource primitive](resource-guide.md).
|
||||
2. Events in the [reactive language](language-guide.md).
|
||||
|
||||
Each resource primitive in mgmt can test (check) and set (apply) the desired
|
||||
state that was requested of it. This is familiar to what is common with existing
|
||||
tools such as `Puppet`, `Ansible`, `Chef`, `Terraform`, etc... In addition,
|
||||
`mgmt` can also **watch** the state and detect changes. As a result, it never
|
||||
has to waste time and cpu resources by polling to test and set state, leading to
|
||||
a design which is algorithmically much faster than the existing generation of
|
||||
tools.
|
||||
|
||||
To describe the set of resources to apply, mgmt describes this collection with a
|
||||
language. In order to model the time component of infrastructure, we use a
|
||||
special kind of language called an [FRP](https://en.wikipedia.org/wiki/Functional_reactive_programming).
|
||||
This language has a built-in concept that we call "events", and which means that
|
||||
we re-evaluate the relevant portions of the code whenever a value or function
|
||||
has an event that tells us that it changed. The `R` in `FRP` stands for
|
||||
reactive. This is similar to how a spreadsheet updates dependent cells when a
|
||||
pre-requisite value is modified. [This article](https://en.wikipedia.org/wiki/Reactive_programming)
|
||||
provides a bit more background.
|
||||
|
||||
Whenever any of the streams of values in the language change, the program is
|
||||
partially re-evaluated. The output of any mgmt program is a [DAG](https://en.wikipedia.org/wiki/Directed_acyclic_graph)
|
||||
of resources, or more precisely, a stream of resource graphs. Since we have
|
||||
events per-resource, we can efficiently switch from one desired-state resource
|
||||
graph to the next without re-checking their individual states, since we've been
|
||||
monitoring them all along.
|
||||
|
||||
One side-effect of all this, is that if a rogue systems administrator manually
|
||||
changes the state of any managed resource, mgmt will detect this and attempt to
|
||||
revert the change. This makes for excellent live demos, but is not the primary
|
||||
design goal. It is a consequence of tracking state so that graph changes are
|
||||
efficient. We implement the event detection via an intentional per-resource
|
||||
[main loop](https://en.wikipedia.org/wiki/Event_loop) which can enable other
|
||||
interesting functionality too!
|
||||
|
||||
Make sure to get rid of your rogue sysadmin! ;)
|
||||
|
||||
### Do I need to run `mgmt` as `root`?
|
||||
|
||||
No and yes. It depends. Nothing in mgmt explicitly requires root in the design,
|
||||
however mgmt will require root only if the changes to your system that you want
|
||||
it to make require root.
|
||||
|
||||
For example, if you use it to manage files that require root access to modify,
|
||||
then you'll need root. If you only use it to manage files and resources
|
||||
elsewhere, then it shouldn't need root. Many resources are perfectly usable
|
||||
without root, and virtually all of my live demos are done without root.
|
||||
|
||||
### How can I run `mgmt` on-demand, or in `cron`, instead of continuously?
|
||||
|
||||
By default, `mgmt` will run continuously in an attempt to keep your machine in a
|
||||
converged state, even as external forces change the current state, or as your
|
||||
time-varying desired state changes over time. (You can write code in the mgmt
|
||||
language which will let you describe a desired state which might change over
|
||||
time.)
|
||||
|
||||
Some users might prefer to only run `mgmt` on-demand manually, or at a set
|
||||
interval via a tool like `cron`. In order to do so, `mgmt` must have a way to
|
||||
shut itself down after a single "run". This feature is possible with the
|
||||
`--converged-timeout` flag. You may specify this flag, along with a number of
|
||||
seconds as the argument, and when there has been no activity for that many
|
||||
seconds, the program will shutdown.
|
||||
|
||||
Alternatively, while it is not recommended, if you'd like to ensure the program
|
||||
never runs for longer that a specific number of seconds, you can ask it to
|
||||
shutdown after that time interval using the `--max-runtime` flag. This also
|
||||
requires a number of seconds as an argument.
|
||||
|
||||
#### Example:
|
||||
|
||||
```
|
||||
./mgmt run lang examples/lang/hello0.mcl --converged-timeout=5
|
||||
```
|
||||
|
||||
### Can I run `mgmt` for type-checking only?
|
||||
|
||||
Yes, you can, add the `--only-unify` option to the lang frontend while using the
|
||||
`run` command, and it will exit after type unification.
|
||||
|
||||
#### Example:
|
||||
|
||||
```
|
||||
./mgmt run --tmp-prefix lang --only-unify examples/lang/hello0.mcl
|
||||
```
|
||||
|
||||
It will also print how long it took on either success or failure. Keep in mind
|
||||
that even if you pass type unification, an `mgmt` run can still fail later on
|
||||
for other reasons, although these are mostly runtime considerations.
|
||||
|
||||
### Why is type unification happening twice with `mgmt run`?
|
||||
|
||||
When you use the `run` action, it runs all of the compile time checks (including
|
||||
type unification) that are possible, and then packages everything up into a
|
||||
deployable object and runs the same mechanism that `mgmt deploy` uses, sending
|
||||
the deploy to itself. At this point, mgmt starts up as a server, and receives
|
||||
the deploy. It will then need to run type unification again before running the
|
||||
code.
|
||||
|
||||
You can skip the first type unification check by adding the `--skip-unify`
|
||||
option to the lang frontend when using the `run` command.
|
||||
|
||||
You can also skip this check when running the `deploy` action, but if your code
|
||||
doesn't pass, you might be deploying broken code. This is not recommended.
|
||||
|
||||
#### Example:
|
||||
|
||||
```
|
||||
./mgmt run --tmp-prefix lang --skip-unify examples/lang/hello0.mcl
|
||||
```
|
||||
|
||||
### Why does my file resource error with `no such file or directory`?
|
||||
|
||||
If you create a file resource and only specify the content like this:
|
||||
|
||||
```
|
||||
file "/tmp/foo" {
|
||||
content => "hello world\n",
|
||||
}
|
||||
```
|
||||
|
||||
Then this will attempt to set the contents of that file to the desired string,
|
||||
but *only* if that file already exists. If you'd like to ensure that it also
|
||||
gets created in case it is not present, then you must also specify the state:
|
||||
|
||||
```
|
||||
file "/tmp/foo" {
|
||||
state => $const.res.file.state.exists,
|
||||
content => "hello world\n",
|
||||
}
|
||||
```
|
||||
|
||||
Similar logic applies for situations when you only specify the `mode` parameter.
|
||||
|
||||
This all turns out to be more safe and "correct", in that it would error and
|
||||
prevent masking an error for a situation when you expected a file to already be
|
||||
at that location. It also turns out to simplify the internals significantly, and
|
||||
remove an ambiguous scenario with the reversible file resource.
|
||||
|
||||
### Package resources error with: "The name is not activatable", what's wrong?
|
||||
|
||||
You may see an error like:
|
||||
|
||||
`main: error running auto edges: The name is not activatable`
|
||||
|
||||
This can happen because the mgmt `pkg` resource uses a library and daemon called
|
||||
`PackageKit` to install packages. If it is not installed, then it cannot do its
|
||||
work. On Fedora system you may wish to run `dnf install /usr/bin/pkcon` or on a
|
||||
Debian system you may wish to run `apt install packagekit-tools`.
|
||||
|
||||
PackageKit is excellent because it provides both an API and an event system to
|
||||
watch the package database for changes, and it abstracts away the differences
|
||||
between the various package managers. If you'd prefer to not need to install
|
||||
this tool, then you can contribute a native `pkg:rpm` and `pkg:deb` resource to
|
||||
mgmt!
|
||||
|
||||
### When running mgmt, it says: "module path error: can't find a module path".
|
||||
|
||||
You might get an error along the lines of:
|
||||
|
||||
```
|
||||
could not set scope: import scope `git://github.com/purpleidea/mgmt/modules/some_module_name/` failed: module path error: can't find a module path
|
||||
```
|
||||
|
||||
This usually means that you haven't specified the directory that mgmt should use
|
||||
when looking for modules. This could happen when using mgmt interactively or
|
||||
when it's being run as a service. In such cases you may want the main invocation
|
||||
to look something like:
|
||||
|
||||
```
|
||||
mgmt run lang --module-path '/etc/mgmt/modules/' /etc/mgmt/main.mcl
|
||||
```
|
||||
|
||||
### I get an error: "cannot open shared object file: No such file or directory".
|
||||
|
||||
Mgmt currently uses two libraries that depend on `.so` files being installed on
|
||||
the host. Those are for `augeas` and `libvirt`. If those dependencies are not
|
||||
present, then mgmt will not run. The complete error might look like:
|
||||
|
||||
```
|
||||
mgmt: error while loading shared libraries: libvirt-lxc.so.0: cannot open shared object file: No such file or directory
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```
|
||||
mgmt: error while loading shared libraries: libaugeas.so.0: cannot open shared object file: No such file or directory
|
||||
```
|
||||
|
||||
or something similar. There are two solutions to this:
|
||||
|
||||
1. Use a build that doesn't include one or both of those features. You can build
|
||||
that like: `GOTAGS="noaugeas novirt nodocker" make build`.
|
||||
|
||||
2. Install those dependencies. On a Fedora machine you might want to run:
|
||||
|
||||
```
|
||||
dnf install libvirt-devel augeas-devel
|
||||
```
|
||||
|
||||
On a Debian machine you might want to run:
|
||||
|
||||
```
|
||||
apt install libvirt-dev libaugeas-dev
|
||||
```
|
||||
|
||||
### Why do function names inside of templates include underscores?
|
||||
|
||||
The golang template library which we use to implement the golang.template() func
|
||||
doesn't support the dot notation, so we import all our normal functions, and
|
||||
just replace dots with underscores. As an example, the standard `datetime.print`
|
||||
function is shown within mcl scripts as datetime_print after being imported.
|
||||
|
||||
### On startup `mgmt` hangs after: `etcd: server: starting...`.
|
||||
|
||||
If you get an error message similar to:
|
||||
|
||||
```
|
||||
etcd: server: starting...
|
||||
etcd: server: start timeout of 1m0s reached
|
||||
etcd: server: close timeout of 15s reached
|
||||
```
|
||||
|
||||
But nothing happens afterwards, this can be due to a corrupt etcd storage
|
||||
directory. Each etcd server embedded in mgmt must have a special directory where
|
||||
it stores local state. It must not be shared by more than one individual member.
|
||||
This dir is typically `/var/lib/mgmt/etcd/member/`. If you accidentally use it
|
||||
(for example during testing) with a different cluster view, then you can corrupt
|
||||
it. This can happen if you use it with more than one different hostname.
|
||||
|
||||
The solution is to avoid making this mistake, and if there is no important data
|
||||
saved, you can remove the etcd member dir and start over.
|
||||
|
||||
### On running `make` to build a new version, it errors with: `Text file busy`.
|
||||
|
||||
If you get an error like:
|
||||
|
||||
```
|
||||
cp: cannot create regular file 'mgmt': Text file busy
|
||||
```
|
||||
|
||||
This can happen if you ran `make build` (or just `make`) when there was already
|
||||
an instance of mgmt running, or if a related file locking issue occurred. To
|
||||
solve this, shutdown and running mgmt process, run `rm mgmt` to remove the file,
|
||||
and then get a new one by running `make` again.
|
||||
|
||||
### Type unification error with string interpolation.
|
||||
|
||||
Look carefully at the following code:
|
||||
|
||||
```
|
||||
$num = 42
|
||||
print "hello" {
|
||||
msg => "My favourite number is ${num}",
|
||||
}
|
||||
```
|
||||
|
||||
What's actually happening is that we can't unify `str + int`, because our
|
||||
addition operator only supports `str + str`, `int + int`, and `float + float`.
|
||||
The string interpolation feature can only combine strings. One solution is to
|
||||
instead write:
|
||||
|
||||
```
|
||||
$num = "42" # now this is a string
|
||||
print "hello" {
|
||||
msg => "My favourite number is ${num}",
|
||||
}
|
||||
```
|
||||
|
||||
The first example will usually error with something along the lines of:
|
||||
|
||||
`unify error with: topLevel(func() { <built-in:concat> }): type error: int != str`
|
||||
|
||||
Now you know why this specific case doesn't work! We may reconsider allowing
|
||||
other types to be pulled into interpolation in the future. If you have a good
|
||||
case for this, then let us know.
|
||||
|
||||
### The run and deploy commands don't parse correctly when used with `--seeds`.
|
||||
|
||||
If you're running a command with `--seeds`, `--server-urls`, or `--client-urls`,
|
||||
then make sure you are using an equals sign between the flag name and the value.
|
||||
For example, if you were to run:
|
||||
|
||||
```
|
||||
# wrong invocation!
|
||||
mgmt deploy --no-git --seeds http://127.0.0.1:2379 lang code/test.mcl
|
||||
```
|
||||
|
||||
Then the `--seeds` flag would interpret `lang` and `code/test.mcl` as additional
|
||||
seeds. This flag as well as the other aforementioned ones all accept multiple
|
||||
values. Use an equals sign to guarantee you enter the correct data, eg:
|
||||
|
||||
```
|
||||
# better invocation! (note the equals sign)
|
||||
mgmt deploy --no-git --seeds=http://127.0.0.1:2379 lang code/test.mcl
|
||||
```
|
||||
|
||||
This is caused by a parsing peculiarity of the CLI library that we are using.
|
||||
This is tracked upstream at: [https://github.com/alexflint/go-arg/issues/239](https://github.com/alexflint/go-arg/issues/239).
|
||||
We have a workaround in place to mitigate it and attempt to show you a helpful
|
||||
error message, but it's also documented here in the meantime. The error you will
|
||||
see is: `cli parse error: missing equals sign for list element`.
|
||||
|
||||
### The docs speaks of `--remote` but the CLI errors out?
|
||||
|
||||
The `--remote` flag existed in an earlier version of mgmt. It was removed and
|
||||
will be replaced with a more powerful version, which is a "remote" resource. The
|
||||
code is mostly ready but it's not finished. If you'd like to help finish it or
|
||||
sponsor the work, please let me know.
|
||||
|
||||
### Does this support Windows? OSX? GNU Hurd?
|
||||
|
||||
Mgmt probably works best on Linux, because that's what most developers use for
|
||||
serious automation workloads. Support for non-Linux operating systems isn't a
|
||||
high priority of mine, but we're happy to accept patches for missing features
|
||||
or resources that you think would make sense on your favourite platform.
|
||||
|
||||
### Why aren't you using `glide`, `godep` or `go mod` for dependency management?
|
||||
|
||||
Vendoring dependencies means that as the git master branch of each dependency
|
||||
marches on, you're left behind using an old version. As a result, bug fixes and
|
||||
improvements are not automatically brought into the project. Instead, we run our
|
||||
complete test suite against the entire project (with the latest dependencies)
|
||||
[every 24 hours](https://docs.travis-ci.com/user/cron-jobs/) to ensure that it
|
||||
all still works.
|
||||
|
||||
Occasionally a dependency breaks API and causes a failure. In those situations,
|
||||
we're notified almost immediately, it's easy to see exactly which commit caused
|
||||
the breakage, and we can either quickly notify the author (if it was a mistake)
|
||||
or update our code if it was a sensible change. This also puts less burden on
|
||||
authors to support old, legacy versions of their software unnecessarily.
|
||||
|
||||
Historically, we've had approximately one such breakage per year, which were all
|
||||
detected and fixed within a few hours. The cost of these small, rare,
|
||||
interruptions is much less expensive than having to periodically move every
|
||||
dependency in the project to the latest versions. Some examples of this include:
|
||||
|
||||
* We caught the `go-bindata` swap before it was publicly known, and fixed it in:
|
||||
[adbe9c7be178898de3645b0ed17ed2ca06646017](https://github.com/purpleidea/mgmt/commit/adbe9c7be178898de3645b0ed17ed2ca06646017).
|
||||
|
||||
* We caught the `codegangsta/cli` API change improvement, and fixed it in:
|
||||
[ab73261fd4e98cf7ecb08066ad228a8f559ba16a](https://github.com/purpleidea/mgmt/commit/ab73261fd4e98cf7ecb08066ad228a8f559ba16a).
|
||||
|
||||
* We caught an un-announced libvirt API change, and promptly fixed it in:
|
||||
[95cb94a03958a9d2ebf01df0821a8c13a4f3a28c](https://github.com/purpleidea/mgmt/commit/95cb94a03958a9d2ebf01df0821a8c13a4f3a28c).
|
||||
|
||||
If we choose responsible dependencies, then it usually means that those authors
|
||||
are also responsible with their changes to API and to git master. If we ever
|
||||
find that it's not the case, then we will either switch that dependency to a
|
||||
more responsible version, or fork it if necessary.
|
||||
|
||||
Occasionally, we want to pin a dependency to a particular version. This can
|
||||
happen if the project treats `git master` as an unstable branch, or because a
|
||||
dependency needs a newer version of golang than the minimum that we require for
|
||||
our project. In those cases it's sensible to assume the technical debt, and
|
||||
vendor the dependency. The common tools such as `glide` and `godep` work by
|
||||
requiring you install their software, and by either storing a yaml file with the
|
||||
version of that dependency in your repository, and/or copying all of that code
|
||||
into git and explicitly storing it. This project thinks that all of these
|
||||
solutions are wasteful and unnecessary, particularly when an existing elegant
|
||||
solution already exists: `[git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules)`.
|
||||
|
||||
The advantages of using `git submodules` are three-fold:
|
||||
1. You already have the required tools installed.
|
||||
2. You only store a pointer to the dependency, not additional files or code.
|
||||
3. The git submodule tools let you easily switch dependency versions, see diff
|
||||
output, and responsibly plan and test your versions bumps with ease.
|
||||
|
||||
Don't blindly use the tools that others tell you to. Learn what they do, think
|
||||
for yourself, and become a power user today! That process led us to using
|
||||
`git submodules`. Hopefully you'll come to the same conclusions that we did.
|
||||
|
||||
**UPDATE:**
|
||||
|
||||
After golang made it virtually impossible to build without `go.mod` stuff, we've
|
||||
switched to it since golang 1.16. I still think the above approach was better,
|
||||
and that the `go mod` tooling should have been a layer on top of git submodules
|
||||
so that we don't grow yet another lock file format, and existing folks who are
|
||||
comfortable with `git` can use those tools directly.
|
||||
|
||||
### Did you know that there is a band named `MGMT`?
|
||||
|
||||
I didn't realize this when naming the project, and it is accidental. After much
|
||||
anguishing, I chose the name because it was short and I thought it was
|
||||
appropriately descriptive. If you need a less ambiguous search term or phrase,
|
||||
you can try using `mgmtconfig` or `mgmt config`.
|
||||
|
||||
It also doesn't stand for
|
||||
[Methyl Guanine Methyl Transferase](https://en.wikipedia.org/wiki/O-6-methylguanine-DNA_methyltransferase)
|
||||
which definitely existed before the band did.
|
||||
|
||||
### You didn't answer my question, or I have a question!
|
||||
|
||||
It's best to ask on [Matrix](https://matrix.to/#/#mgmtconfig:matrix.org) to see
|
||||
if someone can help. If you don't get a response there, you can send a patch to
|
||||
this documentation. I'll merge your question, and add a patch with the answer!
|
||||
For news and updates, subscribe to the [mailing list](https://www.redhat.com/mailman/listinfo/mgmtconfig-list).
|
||||
361
docs/function-guide.md
Normal file
361
docs/function-guide.md
Normal file
@@ -0,0 +1,361 @@
|
||||
# Function guide
|
||||
|
||||
## Overview
|
||||
|
||||
The `mgmt` tool has built-in functions which add useful, reactive functionality
|
||||
to the language. This guide describes the different function API's that are
|
||||
available. It is meant to instruct developers on how to write new functions.
|
||||
Since `mgmt` and the core functions are written in golang, some prior golang
|
||||
knowledge is assumed.
|
||||
|
||||
## Theory
|
||||
|
||||
Functions in `mgmt` are similar to functions in other languages, however they
|
||||
also have a [reactive](https://en.wikipedia.org/wiki/Functional_reactive_programming)
|
||||
component. Our functions can produce events over time, and there are different
|
||||
ways to write functions. For some background on this design, please read the
|
||||
[original article](https://purpleidea.com/blog/2018/02/05/mgmt-configuration-language/)
|
||||
on the subject.
|
||||
|
||||
## Native Functions
|
||||
|
||||
Native functions are functions which are implemented in the mgmt language
|
||||
itself. These are currently not available yet, but are coming soon. Stay tuned!
|
||||
|
||||
## Simple Function API
|
||||
|
||||
Most functions should be implemented using the simple function API. This API
|
||||
allows you to implement simple, static, [pure](https://en.wikipedia.org/wiki/Pure_function)
|
||||
functions that don't require you to write much boilerplate code. They will be
|
||||
automatically re-evaluated as needed when their input values change. These will
|
||||
all be automatically made available as helper functions within mgmt templates,
|
||||
and are also available for use anywhere inside mgmt programs.
|
||||
|
||||
You'll need some basic knowledge of using the [`types`](https://github.com/purpleidea/mgmt/tree/master/lang/types)
|
||||
library which is included with mgmt. This library lets you interact with the
|
||||
available types and values in the mgmt language. It is very easy to use, and
|
||||
should be fairly intuitive. Most of what you'll need to know can be inferred
|
||||
from looking at example code.
|
||||
|
||||
To implement a function, you'll need to create a file that imports the
|
||||
[`lang/funcs/simple/`](https://github.com/purpleidea/mgmt/tree/master/lang/funcs/simple/)
|
||||
module. It should probably get created in the correct directory inside of:
|
||||
[`lang/core/`](https://github.com/purpleidea/mgmt/tree/master/lang/core/). The
|
||||
function should be implemented as a `simple.Scaffold` in our API. It is then
|
||||
registered with the engine during `init()`. An example explains it best:
|
||||
|
||||
### Example
|
||||
|
||||
```golang
|
||||
package simple
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/purpleidea/mgmt/lang/funcs/simple"
|
||||
"github.com/purpleidea/mgmt/lang/types"
|
||||
)
|
||||
|
||||
// you must register your functions in init when the program starts up
|
||||
func init() {
|
||||
// Example function that squares an int and prints out answer as an str.
|
||||
|
||||
simple.ModuleRegister(ModuleName, "talkingsquare", &simple.Scaffold{
|
||||
T: types.NewType("func(int) str"), // declare the signature
|
||||
F: func(ctx context.Context, input []types.Value) (types.Value, error) {
|
||||
i := input[0].Int() // get first arg as an int64
|
||||
// must return the above specified value
|
||||
return &types.StrValue{
|
||||
V: fmt.Sprintf("%d^2 is %d", i, i * i),
|
||||
}, nil // no serious errors occurred
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This simple function accepts one `int` as input, and returns one `str`.
|
||||
Functions can have zero or more inputs, and must have exactly one output. You
|
||||
must be sure to use the `types` library correctly, since if you try and access
|
||||
an input which should not exist (eg: `input[2]`, when there are only two
|
||||
that are expected), then you will cause a panic. If you have declared that a
|
||||
particular argument is an `int` but you try to read it with `.Bool()` you will
|
||||
also cause a panic. Lastly, make sure that you return a value in the correct
|
||||
type or you will also cause a panic!
|
||||
|
||||
If anything goes wrong, you can return an error, however this will cause the
|
||||
mgmt engine to shutdown. It should be seen as the equivalent to calling a
|
||||
`panic()`, however it is safer because it brings the engine down cleanly.
|
||||
Ideally, your functions should never need to error. You should never cause a
|
||||
real `panic()`, since this could have negative consequences to the system.
|
||||
|
||||
### Example
|
||||
|
||||
```golang
|
||||
package simple
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/purpleidea/mgmt/lang/funcs/simple"
|
||||
"github.com/purpleidea/mgmt/lang/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// This is the actual definition of the `len` function.
|
||||
simple.Register("len", &simple.Scaffold{
|
||||
T: types.NewType("func(?1) int"), // contains a unification var
|
||||
C: simple.TypeMatch([]string{ // match on any of these sigs
|
||||
"func(str) int",
|
||||
"func([]?1) int",
|
||||
"func(map{?1: ?2}) int",
|
||||
}),
|
||||
// The implementation is left as an exercise for the reader.
|
||||
F: Len,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Simple Polymorphic Function API
|
||||
|
||||
Most functions should be implemented using the simple function API. If they need
|
||||
to have multiple polymorphic forms under the same name, with each resultant type
|
||||
match needing to be paired to a different implementation, then you can use this
|
||||
API. This is useful for situations when the functions differ in output type
|
||||
only.
|
||||
|
||||
## Function API
|
||||
|
||||
To implement a reactive function in `mgmt` it must satisfy the
|
||||
[`Func`](https://github.com/purpleidea/mgmt/blob/master/lang/interfaces/func.go)
|
||||
interface. Using the [Simple Function API](#simple-function-api) is preferable
|
||||
if it meets your needs. Most functions will be able to use that API. If you
|
||||
really need something more powerful, then you can use the regular function API.
|
||||
What follows are each of the method signatures and a description of each.
|
||||
|
||||
### Info
|
||||
|
||||
```golang
|
||||
Info() *interfaces.Info
|
||||
```
|
||||
|
||||
This returns some information about the function. It is necessary so that the
|
||||
compiler can type check the code correctly, and know what optimizations can be
|
||||
performed. This is usually the first method which is called by the engine.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
func (obj *FooFunc) Info() *interfaces.Info {
|
||||
return &interfaces.Info{
|
||||
Pure: true,
|
||||
Sig: types.NewType("func(a int) str"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Init
|
||||
|
||||
```golang
|
||||
Init(init *interfaces.Init) error
|
||||
```
|
||||
|
||||
This is called to initialize the function. If something goes wrong, it should
|
||||
return an error. It is passed a struct that contains all the important
|
||||
information and pointers that it might need to work with throughout its
|
||||
lifetime. As a result, it will need to save a copy to that pointer for future
|
||||
use in the other methods.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Init runs some startup code for this function.
|
||||
func (obj *FooFunc) Init(init *interfaces.Init) error {
|
||||
obj.init = init
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Call
|
||||
|
||||
Call is run when you want to return a new value from the function. It takes the
|
||||
input arguments to the function.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
func (obj *FooFunc) Call(ctx context.Context, args []types.Value) (types.Value, error) {
|
||||
return &types.StrValue{ // Our type system "str" (string) value.
|
||||
V: strconv.FormatInt(args[0].Int(), 10), // a golang string
|
||||
}, nil
|
||||
}
|
||||
```
|
||||
|
||||
### Stream
|
||||
|
||||
```golang
|
||||
Stream(context.Context) error
|
||||
```
|
||||
|
||||
`Stream` is where any evented work is done. This method is started by the
|
||||
function engine. It will run this function once. It should call the
|
||||
`obj.init.Event()` method when it believes the function engine should run
|
||||
`Call()` again.
|
||||
|
||||
Implementing this is not required if you don't have events.
|
||||
|
||||
If the `ctx` closes, you must shutdown as soon as possible.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Stream starts a mainloop and runs Event when it's time to Call() again.
|
||||
func (obj *FooFunc) Stream(ctx context.Context) error {
|
||||
|
||||
ticker := time.NewTicker(time.Duration(1) * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
// streams must generate an initial event on startup
|
||||
// even though ticker will send one, we want to be faster to first event
|
||||
startChan := make(chan struct{}) // start signal
|
||||
close(startChan) // kick it off!
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-startChan:
|
||||
startChan = nil // disable
|
||||
|
||||
case <-ticker.C: // received the timer event
|
||||
// pass
|
||||
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := obj.init.Event(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Further considerations
|
||||
|
||||
There is some additional information that any function author will need to know.
|
||||
Each issue is listed separately below!
|
||||
|
||||
### Function struct
|
||||
|
||||
Each function will implement methods as pointer receivers on a function struct.
|
||||
The naming convention for resources is that they end with a `Func` suffix.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
type FooFunc struct {
|
||||
init *interfaces.Init
|
||||
|
||||
// this space can be used if needed
|
||||
}
|
||||
```
|
||||
|
||||
### Function registration
|
||||
|
||||
All functions must be registered with the engine so that they can be found. This
|
||||
also ensures they can be encoded and decoded. Make sure to include the following
|
||||
code snippet for this to work.
|
||||
|
||||
```golang
|
||||
import "github.com/purpleidea/mgmt/lang/funcs"
|
||||
|
||||
func init() { // special golang method that runs once
|
||||
funcs.Register("foo", func() interfaces.Func { return &FooFunc{} })
|
||||
}
|
||||
```
|
||||
|
||||
Functions inside of built-in modules will need to use the `ModuleRegister`
|
||||
method instead.
|
||||
|
||||
```golang
|
||||
// moduleName is already set to "math" by the math package. Do this in `init`.
|
||||
funcs.ModuleRegister(moduleName, "cos", func() interfaces.Func {
|
||||
return &CosFunc{}
|
||||
})
|
||||
```
|
||||
|
||||
### Composite functions
|
||||
|
||||
Composite functions are functions which import one or more existing functions.
|
||||
This is useful to prevent code duplication in higher level function scenarios.
|
||||
Unfortunately no further documentation about this subject has been written. To
|
||||
expand this section, please send a patch! Please contact us if you'd like to
|
||||
work on a function that uses this feature, or to add it to an existing one!
|
||||
We don't expect this functionality to be particularly useful or common, as it's
|
||||
probably easier and preferable to simply import common golang library code into
|
||||
multiple different functions instead.
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
(Send your questions as a patch to this FAQ! I'll review it, merge it, and
|
||||
respond by commit with the answer.)
|
||||
|
||||
### Can I use global variables?
|
||||
|
||||
Probably not. You must assume that multiple copies of your function may be used
|
||||
at the same time. If they require a global variable, it's likely this won't
|
||||
work. Instead it's probably better to use a struct local variable if you need to
|
||||
store some state.
|
||||
|
||||
There might be some rare instances where a global would be acceptable, but if
|
||||
you need one of these, you're probably already an internals expert. If you think
|
||||
they need to lock or synchronize so as to not overwhelm an external resource,
|
||||
then you have to be especially careful not to cause deadlocking the mgmt engine.
|
||||
|
||||
### Can I write functions in a different language?
|
||||
|
||||
Currently `golang` is the only supported language for built-in functions. We
|
||||
might consider allowing external functions to be imported in the future. This
|
||||
will likely require a language that can expose a C-like API, such as `python` or
|
||||
`ruby`. Custom `golang` functions are already possible when using mgmt as a lib.
|
||||
|
||||
### What new functions need writing?
|
||||
|
||||
There are still many ideas for new functions that haven't been written yet. If
|
||||
you'd like to contribute one, please contact us and tell us about your idea!
|
||||
|
||||
### Can I generate many different `FuncValue` implementations from one function?
|
||||
|
||||
Yes, you can use a function generator in `golang` to build multiple different
|
||||
implementations from the same function generator. You just need to implement a
|
||||
function which *returns* a `golang` type of `func([]types.Value) (types.Value, error)`
|
||||
which is what `FuncValue` expects. The generator function can use any input it
|
||||
wants to build the individual functions, thus helping with code reuse.
|
||||
|
||||
### How do I determine the signature of my simple, polymorphic function?
|
||||
|
||||
The determination of the input portion of the function signature can be
|
||||
determined by inspecting the length of the input, and the specific type each
|
||||
value has. Length is done in the standard `golang` way, and the type of each
|
||||
element can be ascertained with the `Type()` method available on every value.
|
||||
|
||||
Knowing the output type is trickier. If it can not be inferred in some manner,
|
||||
then the only way is to keep track of this yourself. You can use a function
|
||||
generator to build your `FuncValue` implementations, and pass in the unique
|
||||
signature to each one as you are building them. Using a generator is a common
|
||||
technique which was mentioned previously.
|
||||
|
||||
One obvious situation where this might occur is if your function doesn't take
|
||||
any inputs! An example `math.fortytwo()` function was implemented that
|
||||
demonstrates the use of function generators to pass the type signatures into the
|
||||
implementations.
|
||||
|
||||
### Where can I find more information about mgmt?
|
||||
|
||||
Additional blog posts, videos and other material [is available!](https://github.com/purpleidea/mgmt/blob/master/docs/on-the-web.md).
|
||||
|
||||
## Suggestions
|
||||
|
||||
If you have any ideas for API changes or other improvements to function writing,
|
||||
please let us know! We're still pre 1.0 and pre 0.1 and happy to break API in
|
||||
order to get it right!
|
||||
795
docs/generate.go
Normal file
795
docs/generate.go
Normal file
@@ -0,0 +1,795 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package docs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
cliUtil "github.com/purpleidea/mgmt/cli/util"
|
||||
docsUtil "github.com/purpleidea/mgmt/docs/util"
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
engineUtil "github.com/purpleidea/mgmt/engine/util"
|
||||
"github.com/purpleidea/mgmt/lang/funcs"
|
||||
"github.com/purpleidea/mgmt/lang/interfaces"
|
||||
"github.com/purpleidea/mgmt/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// JSONSuffix is the output extension for the generated documentation.
|
||||
JSONSuffix = ".json"
|
||||
)
|
||||
|
||||
// Generate is the main entrypoint for this command. It generates everything.
|
||||
type Generate struct {
|
||||
*cliUtil.DocsGenerateArgs // embedded config
|
||||
Config // embedded Config
|
||||
|
||||
// Program is the name of this program, usually set at compile time.
|
||||
Program string
|
||||
|
||||
// Version is the version of this program, usually set at compile time.
|
||||
Version string
|
||||
|
||||
// Debug represents if we're running in debug mode or not.
|
||||
Debug bool
|
||||
|
||||
// Logf is a logger which should be used.
|
||||
Logf func(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// Main runs everything for this setup item.
|
||||
func (obj *Generate) Main(ctx context.Context) error {
|
||||
if err := obj.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := obj.Run(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate verifies that the structure has acceptable data stored within.
|
||||
func (obj *Generate) Validate() error {
|
||||
if obj == nil {
|
||||
return fmt.Errorf("data is nil")
|
||||
}
|
||||
if obj.Program == "" {
|
||||
return fmt.Errorf("program is empty")
|
||||
}
|
||||
if obj.Version == "" {
|
||||
return fmt.Errorf("version is empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run performs the desired actions to generate the documentation.
|
||||
func (obj *Generate) Run(ctx context.Context) error {
|
||||
|
||||
outputFile := obj.DocsGenerateArgs.Output
|
||||
if outputFile == "" || !strings.HasSuffix(outputFile, JSONSuffix) {
|
||||
return fmt.Errorf("must specify output")
|
||||
}
|
||||
// support relative paths too!
|
||||
if !strings.HasPrefix(outputFile, "/") {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outputFile = filepath.Join(wd, outputFile)
|
||||
}
|
||||
|
||||
if obj.Debug {
|
||||
obj.Logf("output: %s", outputFile)
|
||||
}
|
||||
|
||||
// Ensure the directory exists.
|
||||
//d := filepath.Dir(outputFile)
|
||||
//if err := os.MkdirAll(d, 0750); err != nil {
|
||||
// return fmt.Errorf("could not make output dir at: %s", d)
|
||||
//}
|
||||
|
||||
resources, err := obj.genResources()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
functions, err := obj.genFunctions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := &Output{
|
||||
Version: safeVersion(obj.Version),
|
||||
Resources: resources,
|
||||
Functions: functions,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b = append(b, '\n') // needs a trailing newline
|
||||
|
||||
if err := os.WriteFile(outputFile, b, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
obj.Logf("wrote: %s", outputFile)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *Generate) getResourceInfo(kind, filename, structName string) (*ResourceInfo, error) {
|
||||
rootDir := obj.DocsGenerateArgs.RootDir
|
||||
if rootDir == "" {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rootDir = wd + "/" // add a trailing slash
|
||||
}
|
||||
if !strings.HasPrefix(rootDir, "/") || !strings.HasSuffix(rootDir, "/") {
|
||||
return nil, fmt.Errorf("bad root dir: %s", rootDir)
|
||||
}
|
||||
|
||||
// filename might be "noop.go" for example
|
||||
p := filepath.Join(rootDir, engine.ResourcesRelDir, filename)
|
||||
|
||||
fset := token.NewFileSet()
|
||||
|
||||
// f is a: https://golang.org/pkg/go/ast/#File
|
||||
f, err := parser.ParseFile(fset, p, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// mcl field name to golang field name
|
||||
mapping, err := engineUtil.LangFieldNameToStructFieldName(kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// golang field name to mcl field name
|
||||
nameMap, err := util.MapSwap(mapping)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// mcl field name to mcl type
|
||||
typMap, err := engineUtil.LangFieldNameToStructType(kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ri := &ResourceInfo{}
|
||||
// Populate the fields, even if they don't have a comment.
|
||||
ri.Name = structName // golang name
|
||||
ri.Kind = kind // duplicate data
|
||||
ri.File = filename
|
||||
ri.Fields = make(map[string]*ResourceFieldInfo)
|
||||
for mclFieldName, fieldName := range mapping {
|
||||
typ, exists := typMap[mclFieldName]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
ri.Fields[mclFieldName] = &ResourceFieldInfo{
|
||||
Name: fieldName,
|
||||
Type: typ.String(),
|
||||
Desc: "", // empty for now
|
||||
}
|
||||
}
|
||||
|
||||
var previousComment *ast.CommentGroup
|
||||
|
||||
// Walk through the AST...
|
||||
ast.Inspect(f, func(node ast.Node) bool {
|
||||
|
||||
// Comments above the struct appear as a node right _before_ we
|
||||
// find the struct, so if we see one, save it for later...
|
||||
if cg, ok := node.(*ast.CommentGroup); ok {
|
||||
previousComment = cg
|
||||
return true
|
||||
}
|
||||
|
||||
typeSpec, ok := node.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
name := typeSpec.Name.Name // name is now known!
|
||||
|
||||
// If the struct isn't what we're expecting, then move on...
|
||||
if name != structName {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if the TypeSpec is a named struct type that we want...
|
||||
st, ok := typeSpec.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// At this point, we have the struct we want...
|
||||
|
||||
var comment *ast.CommentGroup
|
||||
if typeSpec.Doc != nil {
|
||||
// I don't know how to even get here...
|
||||
comment = typeSpec.Doc // found!
|
||||
|
||||
} else if previousComment != nil {
|
||||
comment = previousComment // found!
|
||||
previousComment = nil
|
||||
}
|
||||
|
||||
ri.Desc = commentCleaner(comment)
|
||||
|
||||
// Iterate over the fields of the struct
|
||||
for _, field := range st.Fields.List {
|
||||
// Check if the field has a comment associated with it
|
||||
if field.Doc == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(field.Names) < 1 { // XXX: why does this happen?
|
||||
continue
|
||||
}
|
||||
|
||||
fieldName := field.Names[0].Name
|
||||
if fieldName == "" { // Can this happen?
|
||||
continue
|
||||
}
|
||||
if isPrivate(fieldName) {
|
||||
continue
|
||||
}
|
||||
|
||||
mclFieldName, exists := nameMap[fieldName]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
ri.Fields[mclFieldName].Desc = commentCleaner(field.Doc)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return ri, nil
|
||||
}
|
||||
|
||||
func (obj *Generate) genResources() (map[string]*ResourceInfo, error) {
|
||||
resources := make(map[string]*ResourceInfo)
|
||||
if obj.DocsGenerateArgs.NoResources {
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
r := engine.RegisteredResourcesNames()
|
||||
sort.Strings(r)
|
||||
for _, kind := range r {
|
||||
metadata, err := docsUtil.LookupResource(kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.HasPrefix(kind, "_") {
|
||||
// TODO: Should we display these somehow?
|
||||
// built-in resource
|
||||
continue
|
||||
}
|
||||
|
||||
ri, err := obj.getResourceInfo(kind, metadata.Filename, metadata.Typename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ri.Name == "" {
|
||||
return nil, fmt.Errorf("empty resource name: %s", kind)
|
||||
}
|
||||
if ri.File == "" {
|
||||
return nil, fmt.Errorf("empty resource file: %s", kind)
|
||||
}
|
||||
if ri.Desc == "" {
|
||||
obj.Logf("empty resource desc: %s", kind)
|
||||
}
|
||||
fields := []string{}
|
||||
for field := range ri.Fields {
|
||||
fields = append(fields, field)
|
||||
}
|
||||
sort.Strings(fields)
|
||||
for _, field := range fields {
|
||||
if ri.Fields[field].Desc == "" {
|
||||
obj.Logf("empty resource (%s) field desc: %s", kind, field)
|
||||
}
|
||||
}
|
||||
|
||||
resources[kind] = ri
|
||||
}
|
||||
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
func (obj *Generate) getFunctionInfo(pkg, name string, metadata *docsUtil.Metadata) (*FunctionInfo, error) {
|
||||
rootDir := obj.DocsGenerateArgs.RootDir
|
||||
if rootDir == "" {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rootDir = wd + "/" // add a trailing slash
|
||||
}
|
||||
if !strings.HasPrefix(rootDir, "/") || !strings.HasSuffix(rootDir, "/") {
|
||||
return nil, fmt.Errorf("bad root dir: %s", rootDir)
|
||||
}
|
||||
if metadata.Filename == "" {
|
||||
return nil, fmt.Errorf("empty filename for: %s.%s", pkg, name)
|
||||
}
|
||||
|
||||
// filename might be "pow.go" for example and contain a rel dir
|
||||
p := filepath.Join(rootDir, funcs.FunctionsRelDir, metadata.Filename)
|
||||
|
||||
fset := token.NewFileSet()
|
||||
|
||||
// f is a: https://golang.org/pkg/go/ast/#File
|
||||
f, err := parser.ParseFile(fset, p, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fi := &FunctionInfo{}
|
||||
fi.Name = metadata.Typename
|
||||
fi.File = metadata.Filename
|
||||
|
||||
var previousComment *ast.CommentGroup
|
||||
found := false
|
||||
|
||||
rawFunc := func(node ast.Node) (*ast.CommentGroup, string) {
|
||||
fd, ok := node.(*ast.FuncDecl)
|
||||
if !ok {
|
||||
return nil, ""
|
||||
}
|
||||
return fd.Doc, fd.Name.Name // name is now known!
|
||||
}
|
||||
|
||||
rawStruct := func(node ast.Node) (*ast.CommentGroup, string) {
|
||||
typeSpec, ok := node.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// Check if the TypeSpec is a named struct type that we want...
|
||||
if _, ok := typeSpec.Type.(*ast.StructType); !ok {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
return typeSpec.Doc, typeSpec.Name.Name // name is now known!
|
||||
}
|
||||
|
||||
// Walk through the AST...
|
||||
ast.Inspect(f, func(node ast.Node) bool {
|
||||
|
||||
// Comments above the struct appear as a node right _before_ we
|
||||
// find the struct, so if we see one, save it for later...
|
||||
if cg, ok := node.(*ast.CommentGroup); ok {
|
||||
previousComment = cg
|
||||
return true
|
||||
}
|
||||
|
||||
doc, name := rawFunc(node) // First see if it's a raw func.
|
||||
if name == "" {
|
||||
doc, name = rawStruct(node) // Otherwise it's a struct.
|
||||
}
|
||||
|
||||
// If the func isn't what we're expecting, then move on...
|
||||
if name != metadata.Typename {
|
||||
return true
|
||||
}
|
||||
|
||||
var comment *ast.CommentGroup
|
||||
if doc != nil {
|
||||
// I don't know how to even get here...
|
||||
comment = doc // found!
|
||||
|
||||
} else if previousComment != nil {
|
||||
comment = previousComment // found!
|
||||
previousComment = nil
|
||||
}
|
||||
|
||||
fi.Desc = commentCleaner(comment)
|
||||
found = true
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if !found {
|
||||
//return nil, nil
|
||||
}
|
||||
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
func (obj *Generate) genFunctions() (map[string]*FunctionInfo, error) {
|
||||
functions := make(map[string]*FunctionInfo)
|
||||
if obj.DocsGenerateArgs.NoFunctions {
|
||||
return functions, nil
|
||||
}
|
||||
|
||||
m := funcs.Map() // map[string]func() interfaces.Func
|
||||
names := []string{}
|
||||
for name := range m {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Slice(names, func(i, j int) bool {
|
||||
a := names[i]
|
||||
b := names[j]
|
||||
// TODO: do a sorted-by-package order.
|
||||
return a < b
|
||||
})
|
||||
|
||||
for _, name := range names {
|
||||
//v := m[name]
|
||||
//fn := v()
|
||||
fn := m[name]()
|
||||
|
||||
// eg: golang/strings.has_suffix
|
||||
sp := strings.Split(name, ".")
|
||||
if len(sp) == 0 {
|
||||
return nil, fmt.Errorf("unexpected empty function")
|
||||
}
|
||||
if len(sp) > 2 {
|
||||
return nil, fmt.Errorf("unexpected function name: %s", name)
|
||||
}
|
||||
n := sp[0]
|
||||
p := sp[0]
|
||||
if len(sp) == 1 { // built-in
|
||||
p = "" // no package!
|
||||
}
|
||||
if len(sp) == 2 { // normal import
|
||||
n = sp[1]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(n, "_") {
|
||||
// TODO: Should we display these somehow?
|
||||
// built-in function
|
||||
continue
|
||||
}
|
||||
|
||||
var sig *string
|
||||
//iface := ""
|
||||
if x := fn.Info().Sig; x != nil {
|
||||
s := x.String()
|
||||
sig = &s
|
||||
//iface = "simple"
|
||||
}
|
||||
|
||||
metadata := &docsUtil.Metadata{}
|
||||
|
||||
// XXX: maybe we need a better way to get this?
|
||||
mdFunc, ok := fn.(interfaces.MetadataFunc)
|
||||
if !ok {
|
||||
// Function doesn't tell us what the data is, let's try
|
||||
// to get it automatically...
|
||||
metadata.Typename = funcs.GetFunctionName(fn) // works!
|
||||
metadata.Filename = "" // XXX: How can we get this?
|
||||
|
||||
// XXX: We only need this back-channel metadata store
|
||||
// because we don't know how to get the filename without
|
||||
// manually writing code in each function. Alternatively
|
||||
// we could add a New() method to each struct and then
|
||||
// we could modify the struct instead of having it be
|
||||
// behind a copy which is needed to get new copies!
|
||||
var err error
|
||||
metadata, err = docsUtil.LookupFunction(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else if mdFunc == nil {
|
||||
// programming error
|
||||
return nil, fmt.Errorf("unexpected empty metadata for function: %s", name)
|
||||
|
||||
} else {
|
||||
metadata = mdFunc.GetMetadata()
|
||||
}
|
||||
|
||||
if metadata == nil {
|
||||
return nil, fmt.Errorf("unexpected nil metadata for function: %s", name)
|
||||
}
|
||||
|
||||
// This may be an empty func name if the function did not know
|
||||
// how to get it. (This is normal for automatic regular funcs.)
|
||||
if metadata.Typename == "" {
|
||||
metadata.Typename = funcs.GetFunctionName(fn) // works!
|
||||
}
|
||||
|
||||
fi, err := obj.getFunctionInfo(p, n, metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// We may not get any fields added if we can't find anything...
|
||||
fi.Name = metadata.Typename
|
||||
fi.Package = p
|
||||
fi.Func = n
|
||||
fi.File = metadata.Filename
|
||||
//fi.Desc = desc
|
||||
fi.Signature = sig
|
||||
|
||||
// Hack for golang generated functions!
|
||||
if strings.HasPrefix(fi.Package, "golang/") && fi.File == "generated_funcs.go" {
|
||||
pkg := fi.Package[len("golang/"):]
|
||||
frag := strings.TrimPrefix(fi.Name, strings.Title(strings.Join(strings.Split(pkg, "/"), ""))) // yuck
|
||||
fi.File = fmt.Sprintf("https://pkg.go.dev/%s#%s", pkg, frag)
|
||||
}
|
||||
|
||||
if fi.Func == "" {
|
||||
return nil, fmt.Errorf("empty function name: %s", name)
|
||||
}
|
||||
if fi.File == "" {
|
||||
return nil, fmt.Errorf("empty function file: %s", name)
|
||||
}
|
||||
if fi.Desc == "" {
|
||||
obj.Logf("empty function desc: %s", name)
|
||||
}
|
||||
if fi.Signature == nil {
|
||||
obj.Logf("empty function sig: %s", name)
|
||||
}
|
||||
|
||||
functions[name] = fi
|
||||
}
|
||||
|
||||
return functions, nil
|
||||
}
|
||||
|
||||
// Output is the type of the final data that will be for the json output.
|
||||
type Output struct {
|
||||
// Version is the sha1 or ref name of this specific version. This is
|
||||
// used if we want to generate documentation with links matching the
|
||||
// correct version. If unspecified then this assumes git master.
|
||||
Version string `json:"version"`
|
||||
|
||||
// Resources contains the collection of every available resource!
|
||||
// FIXME: should this be a list instead?
|
||||
Resources map[string]*ResourceInfo `json:"resources"`
|
||||
|
||||
// Functions contains the collection of every available function!
|
||||
// FIXME: should this be a list instead?
|
||||
Functions map[string]*FunctionInfo `json:"functions"`
|
||||
}
|
||||
|
||||
// ResourceInfo stores some information about each resource.
|
||||
type ResourceInfo struct {
|
||||
// Name is the golang name of this resource.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Kind is the kind of this resource.
|
||||
Kind string `json:"kind"`
|
||||
|
||||
// File is the file name where this resource exists.
|
||||
File string `json:"file"`
|
||||
|
||||
// Desc explains what this resource does.
|
||||
Desc string `json:"description"`
|
||||
|
||||
// Fields is a collection of each resource field and corresponding info.
|
||||
Fields map[string]*ResourceFieldInfo `json:"fields"`
|
||||
}
|
||||
|
||||
// ResourceFieldInfo stores some information about each field in each resource.
|
||||
type ResourceFieldInfo struct {
|
||||
// Name is what this field is called in golang format.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Type is the mcl type for this field.
|
||||
Type string `json:"type"`
|
||||
|
||||
// Desc explains what this field does.
|
||||
Desc string `json:"description"`
|
||||
}
|
||||
|
||||
// FunctionInfo stores some information about each function.
|
||||
type FunctionInfo struct {
|
||||
// Name is the golang name of this function. This may be an actual
|
||||
// function if used by the simple API, or the name of a struct.
|
||||
Name string `json:"name"`
|
||||
|
||||
// Package is the import name to use to get to this function.
|
||||
Package string `json:"package"`
|
||||
|
||||
// Func is the name of the function in that package.
|
||||
Func string `json:"func"`
|
||||
|
||||
// File is the file name where this function exists.
|
||||
File string `json:"file"`
|
||||
|
||||
// Desc explains what this function does.
|
||||
Desc string `json:"description"`
|
||||
|
||||
// Signature is the type signature of this function. If empty then the
|
||||
// signature is not known statically and it may be polymorphic.
|
||||
Signature *string `json:"signature,omitempty"`
|
||||
}
|
||||
|
||||
// commentCleaner takes a comment group and returns it as a clean string. It
|
||||
// removes the spurious newlines and programmer-focused comments. If there are
|
||||
// blank lines, it replaces them with a single newline. The idea is that the
|
||||
// webpage formatter would replace the newline with a <br /> or similar. This
|
||||
// code is a modified alternative of the ast.CommentGroup.Text() function.
|
||||
func commentCleaner(g *ast.CommentGroup) string {
|
||||
if g == nil {
|
||||
return ""
|
||||
}
|
||||
comments := make([]string, len(g.List))
|
||||
for i, c := range g.List {
|
||||
comments[i] = c.Text
|
||||
}
|
||||
|
||||
lines := make([]string, 0, 10) // most comments are less than 10 lines
|
||||
for _, c := range comments {
|
||||
// Remove comment markers.
|
||||
// The parser has given us exactly the comment text.
|
||||
switch c[1] {
|
||||
case '/':
|
||||
//-style comment (no newline at the end)
|
||||
c = c[2:]
|
||||
if len(c) == 0 {
|
||||
// empty line
|
||||
break
|
||||
}
|
||||
if isDevComment(c[1:]) { // get rid of one space
|
||||
continue
|
||||
}
|
||||
if c[0] == ' ' {
|
||||
// strip first space - required for Example tests
|
||||
c = c[1:]
|
||||
break
|
||||
}
|
||||
//if isDirective(c) {
|
||||
// // Ignore //go:noinline, //line, and so on.
|
||||
// continue
|
||||
//}
|
||||
case '*':
|
||||
/*-style comment */
|
||||
c = c[2 : len(c)-2]
|
||||
}
|
||||
|
||||
// Split on newlines.
|
||||
cl := strings.Split(c, "\n")
|
||||
|
||||
// Walk lines, stripping trailing white space and adding to list.
|
||||
for _, l := range cl {
|
||||
lines = append(lines, stripTrailingWhitespace(l))
|
||||
}
|
||||
}
|
||||
|
||||
// Remove leading blank lines; convert runs of interior blank lines to a
|
||||
// single blank line.
|
||||
n := 0
|
||||
for _, line := range lines {
|
||||
if line != "" || n > 0 && lines[n-1] != "" {
|
||||
lines[n] = line
|
||||
n++
|
||||
}
|
||||
}
|
||||
lines = lines[0:n]
|
||||
|
||||
// Concatenate all of these together. Blank lines should be a newline.
|
||||
s := ""
|
||||
for i, line := range lines {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
s += line
|
||||
if i < len(lines)-1 { // Is there another line?
|
||||
if lines[i+1] == "" {
|
||||
s += "\n" // Will eventually be a line break.
|
||||
} else {
|
||||
s += " "
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// TODO: should we use unicode.IsSpace instead?
|
||||
func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' }
|
||||
|
||||
// TODO: should we replace with a strings package stdlib function?
|
||||
func stripTrailingWhitespace(s string) string {
|
||||
i := len(s)
|
||||
for i > 0 && isWhitespace(s[i-1]) {
|
||||
i--
|
||||
}
|
||||
return s[0:i]
|
||||
}
|
||||
|
||||
// isPrivate specifies if a field name is "private" or not.
|
||||
func isPrivate(fieldName string) bool {
|
||||
if fieldName == "" {
|
||||
panic("invalid field name")
|
||||
}
|
||||
x := fieldName[0:1]
|
||||
|
||||
if strings.ToLower(x) == x {
|
||||
return true // it was already private
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// isDevComment tells us that the comment is for developers only!
|
||||
func isDevComment(comment string) bool {
|
||||
if strings.HasPrefix(comment, "TODO:") {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(comment, "FIXME:") {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(comment, "XXX:") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// safeVersion parses the main version string and returns a short hash for us.
|
||||
// For example, we might get a string of 0.0.26-176-gabcdef012-dirty as input,
|
||||
// and we'd want to return abcdef012.
|
||||
func safeVersion(version string) string {
|
||||
const dirty = "-dirty"
|
||||
|
||||
s := version
|
||||
if strings.HasSuffix(s, dirty) { // helpful dirty remover
|
||||
s = s[0 : len(s)-len(dirty)]
|
||||
}
|
||||
|
||||
ix := strings.LastIndex(s, "-")
|
||||
if ix == -1 { // assume we have a standalone version (future proofing?)
|
||||
return s
|
||||
}
|
||||
s = s[ix+1:]
|
||||
|
||||
// From the `git describe` man page: The "g" prefix stands for "git" and
|
||||
// is used to allow describing the version of a software depending on
|
||||
// the SCM the software is managed with. This is useful in an
|
||||
// environment where people may use different SCMs.
|
||||
const g = "g"
|
||||
if strings.HasPrefix(s, g) {
|
||||
s = s[len(g):]
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
17
docs/index.rst
Normal file
17
docs/index.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
.. mgmt documentation master file, created by
|
||||
sphinx-quickstart on Wed Feb 15 21:34:09 2017.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to mgmt's documentation!
|
||||
================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
documentation
|
||||
quick-start-guide
|
||||
resource-guide
|
||||
prometheus
|
||||
puppet-guide
|
||||
1073
docs/language-guide.md
Normal file
1073
docs/language-guide.md
Normal file
File diff suppressed because it is too large
Load Diff
65
docs/on-the-web.md
Normal file
65
docs/on-the-web.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# On the web
|
||||
|
||||
Here is a list of places mgmt has appeared on the web. Feel free to send a patch
|
||||
if we missed something that you think is relevant!
|
||||
|
||||
## Links
|
||||
|
||||
| Author | Format | Subject |
|
||||
|---|---|---|
|
||||
| James Shubin | blog | [Next generation configuration mgmt](https://purpleidea.com/blog/2016/01/18/next-generation-configuration-mgmt/) |
|
||||
| James Shubin | video | [Introductory recording from DevConf.cz 2016](https://www.youtube.com/watch?v=GVhpPF0j-iE&html5=1) |
|
||||
| James Shubin | video | [Introductory recording from CfgMgmtCamp.eu 2016](https://www.youtube.com/watch?v=fNeooSiIRnA&html5=1) |
|
||||
| Julian Dunn | video | [On mgmt at CfgMgmtCamp.eu 2016](https://www.youtube.com/watch?v=kfF9IATUask&t=1949&html5=1) |
|
||||
| Walter Heck | slides | [On mgmt at CfgMgmtCamp.eu 2016](http://www.slideshare.net/olindata/configuration-management-time-for-a-4th-generation/3) |
|
||||
| Marco Marongiu | blog | [On mgmt](http://syslog.me/2016/02/15/leap-or-die/) |
|
||||
| Felix Frank | blog | [From Catalog To Mgmt (on puppet to mgmt "transpiling")](https://ffrank.github.io/features/2016/02/18/from-catalog-to-mgmt/) |
|
||||
| James Shubin | blog | [Automatic edges in mgmt (...and the pkg resource)](https://purpleidea.com/blog/2016/03/14/automatic-edges-in-mgmt/) |
|
||||
| James Shubin | blog | [Automatic grouping in mgmt](https://purpleidea.com/blog/2016/03/30/automatic-grouping-in-mgmt/) |
|
||||
| John Arundel | tweet | [“Puppet’s days are numbered.”](https://twitter.com/bitfield/status/732157519142002688) |
|
||||
| Felix Frank | blog | [Puppet, Meet Mgmt (on puppet to mgmt internals)](https://ffrank.github.io/features/2016/06/12/puppet,-meet-mgmt/) |
|
||||
| Felix Frank | blog | [Puppet Powered Mgmt (puppet to mgmt tl;dr)](https://ffrank.github.io/features/2016/06/19/puppet-powered-mgmt/) |
|
||||
| James Shubin | blog | [Automatic clustering in mgmt](https://purpleidea.com/blog/2016/06/20/automatic-clustering-in-mgmt/) |
|
||||
| James Shubin | video | [Recording from CoreOSFest 2016](https://www.youtube.com/watch?v=KVmDCUA42wc&html5=1) |
|
||||
| James Shubin | video | [Recording from DebConf16](http://meetings-archive.debian.net/pub/debian-meetings/2016/debconf16/Next_Generation_Config_Mgmt.webm) |
|
||||
| Felix Frank | blog | [Edging It All In (puppet and mgmt edges)](https://ffrank.github.io/features/2016/07/12/edging-it-all-in/) |
|
||||
| Felix Frank | blog | [Translating All The Things (puppet to mgmt translation warnings)](https://ffrank.github.io/features/2016/08/19/translating-all-the-things/) |
|
||||
| James Shubin | video | [Recording from systemd.conf 2016](https://www.youtube.com/watch?v=_TowsFAWWRA) |
|
||||
| James Shubin | blog | [Remote execution in mgmt](https://purpleidea.com/blog/2016/10/07/remote-execution-in-mgmt/) |
|
||||
| James Shubin | video | [Recording from High Load Strategy 2016](https://www.youtube.com/watch?v=-4g14KUVPVk) |
|
||||
| James Shubin | video | [Recording from NLUUG 2016](https://www.youtube.com/watch?v=0vO93ni1zos) |
|
||||
| James Shubin | blog | [Send/Recv in mgmt](https://purpleidea.com/blog/2016/12/07/sendrecv-in-mgmt/) |
|
||||
| Julien Pivotto | blog | [Augeas resource for mgmt](https://purpleidea.com/cached/mgmt-augeas.html) (Cached from: https://roidelapluie.be/blog/2017/02/14/mgmt-augeas/) |
|
||||
| James Shubin | blog | [Metaparameters in mgmt](https://purpleidea.com/blog/2017/03/01/metaparameters-in-mgmt/) |
|
||||
| James Shubin | video | [Recording from Incontro DevOps 2017](https://vimeo.com/212241877) |
|
||||
| Yves Brissaud | blog | [mgmt aux HumanTalks Grenoble (french)](http://log.winsos.net/2017/04/12/mgmt-aux-human-talks-grenoble.html) |
|
||||
| James Shubin | video | [Recording from OSDC Berlin 2017](https://www.youtube.com/watch?v=LkEtBVLfygE&html5=1) |
|
||||
| Jonathan Gold | blog | [AWS:EC2 in mgmt](https://jonathangold.ca/blog/aws-ec2-in-mgmt/) |
|
||||
| James Shubin | video | [Recording from OSMC Nuremberg 2017](https://www.youtube.com/watch?v=hSVadQLeplU&html5=1) |
|
||||
| James Shubin | video | [Recording from LCA 2018, Developers Miniconf](https://www.youtube.com/watch?v=OvgGfW0ilbE) |
|
||||
| James Shubin | video | [Recording from LCA 2018, Sysadmin Miniconf](https://www.youtube.com/watch?v=ELq1XOJMIPY) |
|
||||
| James Shubin | video | [Recording from LCA 2018, Main Conference](https://www.youtube.com/watch?v=_9PG64AOQ3w) |
|
||||
| James Shubin | video | [Recording from DevConf.cz 2017](https://www.youtube.com/watch?v=-FPEK08l1Zk) |
|
||||
| James Shubin | video | [Recording from FOSDEM 2018, Config Management Devroom](https://video.fosdem.org/2018/UA2.114/mgmt.webm) |
|
||||
| James Shubin | blog | [Mgmt Configuration Language](https://purpleidea.com/blog/2018/02/05/mgmt-configuration-language/) |
|
||||
| James Shubin | video | [Recording from CfgMgmtCamp.eu 2018](https://www.youtube.com/watch?v=NxObmwZDyrI) |
|
||||
| Jonathan Gold | blog | [Go Netlink and Select](https://jonathangold.ca/blog/go-netlink-and-select/) |
|
||||
| James Shubin | video | [Recording from DevOpsDays Montreal 2018](https://www.youtube.com/watch?v=1i38c5cooHo) |
|
||||
| James Shubin | video | [Recording from FOSDEM Minimalistic Languages Devroom 2019](https://video.fosdem.org/2019/K.4.201/mgmtconfig.webm) |
|
||||
| James Shubin | video | [Recording from FOSDEM Infra Management Devroom 2019](https://video.fosdem.org/2019/UB2.252A/mgmt.webm) |
|
||||
| James Shubin | video | [Recording from FOSDEM Graph Processing Devroom 2019](https://video.fosdem.org/2019/H.1308/graph_mgmt_config.webm) |
|
||||
| James Shubin | video | [Recording from FOSDEM Virtualization Devroom 2019](https://video.fosdem.org/2019/H.2213/vai_real_time_virtualization_automation.webm) |
|
||||
| James Shubin | video | [Recording from FOSDEM Containers Devroom 2019](https://video.fosdem.org/2019/UA2.114/containers_mgmt.webm) |
|
||||
| James Shubin | video | [Recording from FOSDEM Monitoring Devroom 2019](https://video.fosdem.org/2019/UB2.252A/real_time_merging_of_config_management_and_monitoring.webm) |
|
||||
| James Shubin | blog | [Mgmt Configuration Language: Class and Include](https://purpleidea.com/blog/2019/07/26/class-and-include-in-mgmt/) |
|
||||
| James Shubin | video | [Recording from FOSDEM 2020, Main Track (History)](https://video.fosdem.org/2020/Janson/automation.webm) |
|
||||
| James Shubin | video | [Recording from FOSDEM 2020, Infra Management Devroom](https://video.fosdem.org/2020/UA2.120/mgmt.webm) |
|
||||
| James Shubin | video | [Recording from FOSDEM 2020, Minimalistic Languages Devroom](https://video.fosdem.org/2020/AW1.125/mgmtconfigmore.webm) |
|
||||
| James Shubin | video | [Recording from CfgMgmtCamp.eu 2020](https://www.youtube.com/watch?v=Kd7FAORFtsc) |
|
||||
| James Shubin | video | [Recording from CfgMgmtCamp.eu 2023](https://www.youtube.com/watch?v=FeRGRj8w0BU) |
|
||||
| James Shubin | video | [Recording from FOSDEM 2024, Golang Devroom](https://video.fosdem.org/2024/ud2218a/fosdem-2024-2575-single-binary-full-stack-provisioning.mp4) |
|
||||
| James Shubin | video | [Recording from CfgMgmtCamp.eu 2024](https://www.youtube.com/watch?v=vBt9lpGD4bc) |
|
||||
| James Shubin | blog | [Mgmt Configuration Language: Functions](https://purpleidea.com/blog/2024/11/22/functions-in-mgmt/) |
|
||||
| James Shubin | blog | [Modules and imports in mgmt](https://purpleidea.com/blog/2024/12/03/modules-and-imports-in-mgmt/) |
|
||||
| James Shubin | video | [Recording from FOSDEM 2025, Docs Devroom](https://video.fosdem.org/2025/k4201/fosdem-2025-6143-docs-straight-from-the-code-ast-powered-automation.mp4) |
|
||||
| James Shubin | video | [Recording from CfgMgmtCamp.eu 2025](https://www.youtube.com/watch?v=0Oa7CWx4TEA) |
|
||||
66
docs/prometheus.md
Normal file
66
docs/prometheus.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Prometheus support
|
||||
|
||||
Mgmt comes with a built-in prometheus support. It is disabled by default, and
|
||||
can be enabled with the `--prometheus` command line switch.
|
||||
|
||||
By default, the prometheus instance will listen on [`127.0.0.1:9233`][pd]. You
|
||||
can change this setting by using the `--prometheus-listen` cli option:
|
||||
|
||||
To have mgmt prometheus bind interface on 0.0.0.0:45001, use:
|
||||
`./mgmt r --prometheus --prometheus-listen :45001`
|
||||
|
||||
## Metrics
|
||||
|
||||
Mgmt exposes three kinds of resources: _go_ metrics, _etcd_ metrics and _mgmt_
|
||||
metrics.
|
||||
|
||||
### go metrics
|
||||
|
||||
We use the [prometheus go_collector][pgc] to expose go metrics. Those metrics
|
||||
are mainly useful for debugging and perf testing.
|
||||
|
||||
### etcd metrics
|
||||
|
||||
mgmt exposes etcd metrics. Read more in the [upstream documentation][etcdm]
|
||||
|
||||
### mgmt metrics
|
||||
|
||||
Here is a list of the metrics we provide:
|
||||
|
||||
- `mgmt_resources_total`: The number of resources that mgmt is managing
|
||||
- `mgmt_checkapply_total`: The number of CheckApply's that mgmt has run
|
||||
- `mgmt_failures_total`: The number of resources that have failed
|
||||
- `mgmt_failures`: The number of resources that have failed
|
||||
- `mgmt_graph_start_time_seconds`: Start time of the current graph since unix
|
||||
epoch in seconds
|
||||
|
||||
For each metric, you will get some extra labels:
|
||||
|
||||
- `kind`: The kind of mgmt resource
|
||||
|
||||
For `mgmt_checkapply_total`, those extra labels are set:
|
||||
|
||||
- `eventful`: "true" or "false", if the CheckApply triggered some changes
|
||||
- `errorful`: "true" or "false", if the CheckApply reported an error
|
||||
- `apply`: "true" or "false", if the CheckApply ran in apply or noop mode
|
||||
|
||||
## Alerting
|
||||
|
||||
You can use prometheus to alert you upon changes or failures. We do not provide
|
||||
such templates yet, but we plan to provide some examples in this repository.
|
||||
Patches welcome!
|
||||
|
||||
## Grafana
|
||||
|
||||
We do not have grafana dashboards yet. Patches welcome!
|
||||
|
||||
## External resources
|
||||
|
||||
- [prometheus website](https://prometheus.io/)
|
||||
- [prometheus documentation](https://prometheus.io/docs/introduction/overview/)
|
||||
- [prometheus best practices regarding metrics naming](https://prometheus.io/docs/practices/naming/)
|
||||
- [grafana website](http://grafana.org/)
|
||||
|
||||
[pgc]: https://github.com/prometheus/client_golang/blob/master/prometheus/go_collector.go
|
||||
[etcdm]: https://coreos.com/etcd/docs/latest/metrics.html
|
||||
[pd]: https://github.com/prometheus/prometheus/wiki/Default-port-allocations
|
||||
316
docs/puppet-guide.md
Normal file
316
docs/puppet-guide.md
Normal file
@@ -0,0 +1,316 @@
|
||||
# Puppet guide
|
||||
|
||||
`mgmt` can use Puppet as its source for the configuration graph.
|
||||
This document goes into detail on how this works, and lists
|
||||
some pitfalls and limitations.
|
||||
|
||||
For basic instructions on how to use the Puppet support, see
|
||||
the [main documentation](documentation.md#puppet-support).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You need Puppet installed in your system. It is not important how you
|
||||
get it. On the most common Linux distributions, you can use packages
|
||||
from the OS maintainer, or upstream Puppet repositories. An alternative
|
||||
that will also work on OSX is the `puppet` Ruby gem. It also has the
|
||||
advantage that you can install any desired version in your home directory
|
||||
or any other location.
|
||||
|
||||
Any release of Puppet's 3.x and 4.x series should be suitable for use with
|
||||
`mgmt`. Most importantly, make sure to install the `ffrank-mgmtgraph` Puppet
|
||||
module (referred to below as "the translator module").
|
||||
|
||||
```
|
||||
puppet module install ffrank-mgmtgraph
|
||||
```
|
||||
|
||||
Please note that the module is not required on your Puppet master (if you
|
||||
use a master/agent setup). It's needed on the machine that runs `mgmt`.
|
||||
You can install the module on the master anyway, so that it gets distributed
|
||||
to your agents through Puppet's `pluginsync` mechanism.
|
||||
|
||||
### Testing the Puppet side
|
||||
|
||||
The following command should run successfully and print a YAML hash on your
|
||||
terminal:
|
||||
|
||||
```puppet
|
||||
puppet mgmtgraph print --code 'file { "/tmp/mgmt-test": ensure => present }'
|
||||
```
|
||||
|
||||
You can use this CLI to test any manifests before handing them straight
|
||||
to `mgmt`.
|
||||
|
||||
## Writing a suitable manifest
|
||||
|
||||
### Unsupported attributes
|
||||
|
||||
`mgmt` inherited its resource module from Puppet, so by and large, it's quite
|
||||
possible to express `mgmt` graphs in terms of Puppet manifests. However,
|
||||
there isn't (and likely never will be) full feature parity between the
|
||||
respective resource types. In consequence, a manifest can have semantics that
|
||||
cannot be transferred to `mgmt`.
|
||||
|
||||
For example, at the time of writing this, the `file` type in `mgmt` had no
|
||||
notion of permissions (the file `mode`) yet. This lead to the following
|
||||
warning (among others that will be discussed below):
|
||||
|
||||
```
|
||||
$ puppet mgmtgraph print --code 'file { "/tmp/foo": mode => "0600" }'
|
||||
Warning: cannot translate: File[/tmp/foo] { mode => "600" } (attribute is ignored)
|
||||
```
|
||||
|
||||
This is a heads-up for the user, because the resulting `mgmt` graph will
|
||||
in fact not pass this information to the `/tmp/foo` file resource, and
|
||||
`mgmt` will ignore this file's permissions. Including such attributes in
|
||||
manifests that are written expressly for `mgmt` is not sensible and should
|
||||
be avoided.
|
||||
|
||||
### Unsupported resources
|
||||
|
||||
Puppet has a fairly large number of
|
||||
[built-in types](https://www.puppet.com/docs/puppet/8/cheatsheet_core_types.html),
|
||||
and countless more are available through
|
||||
[modules](https://forge.puppet.com/). It's unlikely that all of them will
|
||||
eventually receive native counterparts in `mgmt`.
|
||||
|
||||
When encountering an unknown resource, the translator module will replace
|
||||
it with an `exec` resource in its output. This resource will run the equivalent
|
||||
of a `puppet resource` command to make Puppet apply the original resource
|
||||
itself. This has quite abysmal performance, because processing such a
|
||||
resource requires the forking of at least one Puppet process (two if it
|
||||
is found to be out of sync). This comes with considerable overhead.
|
||||
On most systems, starting up any Puppet command takes several seconds.
|
||||
Compared to the split second that the actual work usually takes,
|
||||
this overhead can amount to several orders of magnitude.
|
||||
|
||||
Avoid Puppet types that `mgmt` does not implement (yet).
|
||||
|
||||
### Avoiding common warnings
|
||||
|
||||
Many resource parameters in Puppet take default values. For the most part,
|
||||
the translator module just ignores them. However, there are cases in which
|
||||
Puppet will default to convenient behavior that `mgmt` cannot quite replicate.
|
||||
For example, translating a plain `file` resource will lead to a warning message:
|
||||
|
||||
```
|
||||
$ puppet mgmtgraph print --code 'file { "/tmp/mgmt-test": }'
|
||||
Warning: File[/tmp/mgmt-test] uses the 'puppet' file bucket, which mgmt cannot
|
||||
do. There will be no backup copies!
|
||||
```
|
||||
|
||||
The reason is that per default, Puppet assumes the following parameter value
|
||||
(among others)
|
||||
|
||||
```puppet
|
||||
file { "/tmp/mgmt-test":
|
||||
backup => 'puppet',
|
||||
}
|
||||
```
|
||||
|
||||
To avoid this, specify the parameter explicitly:
|
||||
|
||||
```bash
|
||||
puppet mgmtgraph print --code 'file { "/tmp/mgmt-test": backup => false }'
|
||||
```
|
||||
|
||||
This is tedious in a more complex manifest. A good simplification is the
|
||||
following [resource default](https://www.puppet.com/docs/puppet/8/lang_defaults)
|
||||
anywhere on the top scope of your manifest:
|
||||
|
||||
```puppet
|
||||
File { backup => false }
|
||||
```
|
||||
|
||||
If you encounter similar warnings from other types and/or parameters,
|
||||
use the same approach to silence them if possible.
|
||||
|
||||
## Configuring Puppet
|
||||
|
||||
Since `mgmt` uses an actual Puppet CLI behind the scenes, you might
|
||||
need to tweak some of Puppet's runtime options in order to make it
|
||||
do what you want. Reasons for this could be among the following:
|
||||
|
||||
* You use the `--puppet agent` variant and need to configure
|
||||
`servername`, `certname` and other master/agent-related options.
|
||||
* You don't want runtime information to end up in the `vardir`
|
||||
that is used by your regular `puppet agent`.
|
||||
* You install specific Puppet modules for `mgmt` in a non-standard
|
||||
location.
|
||||
|
||||
`mgmt` exposes only one Puppet option in order to allow you to
|
||||
control all of them, through its `--puppet-conf` option. It allows
|
||||
you to specify which `puppet.conf` file should be used during
|
||||
translation.
|
||||
|
||||
```
|
||||
mgmt run puppet --puppet /opt/my-manifest.pp --puppet-conf /etc/mgmt/puppet.conf
|
||||
```
|
||||
|
||||
Within this file, you can just specify any needed options in the
|
||||
`[main]` section:
|
||||
|
||||
```
|
||||
[main]
|
||||
server=mgmt-master.example.net
|
||||
vardir=/var/lib/mgmt/puppet
|
||||
```
|
||||
|
||||
## Caveats
|
||||
|
||||
Please see the [README](https://github.com/ffrank/puppet-mgmtgraph/blob/master/README.md)
|
||||
of the translator module for the current state of supported and unsupported
|
||||
language features.
|
||||
|
||||
You should probably make sure to always use the latest release of
|
||||
both `ffrank-mgmtgraph` and `ffrank-yamlresource` (the latter is
|
||||
getting pulled in as a dependency of the former).
|
||||
|
||||
## Using Puppet in conjunction with the mcl lang
|
||||
|
||||
The graph that Puppet generates for `mgmt` can be united with a graph
|
||||
that is created from native `mgmt` code in its mcl language. This is
|
||||
useful when you are in the process of replacing Puppet with mgmt. You
|
||||
can translate your custom modules into mgmt's language one by one,
|
||||
and let mgmt run the current mix.
|
||||
|
||||
Instead of the usual `--puppet-conf` flag and argv for `puppet` and `mcl` input,
|
||||
you need to use alternative flags to make this work:
|
||||
|
||||
* `--lp-lang` to specify the mcl input
|
||||
* `--lp-puppet` to specify the puppet input
|
||||
* `--lp-puppet-conf` to point to the optional puppet.conf file
|
||||
|
||||
`mgmt` will derive a graph that contains all edges and vertices from
|
||||
both inputs. You essentially get two unrelated subgraphs that run in
|
||||
parallel. To form edges between these subgraphs, you have to define
|
||||
special vertices that will be merged. This works through a hard-coded
|
||||
naming scheme.
|
||||
|
||||
### Mixed graph example 1 - No merges
|
||||
|
||||
```mcl
|
||||
# lang
|
||||
file "/tmp/mgmt_dir/" { state => "present" }
|
||||
file "/tmp/mgmt_dir/a" { state => "present" }
|
||||
```
|
||||
|
||||
```puppet
|
||||
# puppet
|
||||
file { "/tmp/puppet_dir": ensure => "directory" }
|
||||
file { "/tmp/puppet_dir/a": ensure => "file" }
|
||||
```
|
||||
|
||||
These very simple inputs (including implicit edges from directory to
|
||||
respective file) result in two subgraphs that do not relate.
|
||||
|
||||
```
|
||||
File[/tmp/mgmt_dir/] -> File[/tmp/mgmt_dir/a]
|
||||
|
||||
File[/tmp/puppet_dir] -> File[/tmp/puppet_dir/a]
|
||||
```
|
||||
|
||||
### Mixed graph example 2 - Merged vertex
|
||||
|
||||
In order to have merged vertices in the resulting graph, you will
|
||||
need to include special resources and classes in the respective
|
||||
input code.
|
||||
|
||||
* On the lang side, add `noop` resources with names starting in `puppet_`.
|
||||
* On the Puppet side, add **empty** classes with names starting in `mgmt_`.
|
||||
|
||||
```mcl
|
||||
# lang
|
||||
noop "puppet_handover_to_mgmt" {}
|
||||
file "/tmp/mgmt_dir/" { state => "present" }
|
||||
file "/tmp/mgmt_dir/a" { state => "present" }
|
||||
|
||||
Noop["puppet_handover_to_mgmt"] -> File["/tmp/mgmt_dir/"]
|
||||
```
|
||||
|
||||
```puppet
|
||||
# puppet
|
||||
class mgmt_handover_to_mgmt {}
|
||||
include mgmt_handover_to_mgmt
|
||||
|
||||
file { "/tmp/puppet_dir": ensure => "directory" }
|
||||
file { "/tmp/puppet_dir/a": ensure => "file" }
|
||||
|
||||
File["/tmp/puppet_dir/a"] -> Class["mgmt_handover_to_mgmt"]
|
||||
```
|
||||
|
||||
The new `noop` resource is merged with the new class, resulting in
|
||||
the following graph:
|
||||
|
||||
```
|
||||
File[/tmp/puppet_dir] -> File[/tmp/puppet_dir/a]
|
||||
|
|
||||
V
|
||||
Noop[handover_to_mgmt]
|
||||
|
|
||||
V
|
||||
File[/tmp/mgmt_dir/] -> File[/tmp/mgmt_dir/a]
|
||||
```
|
||||
|
||||
You put all your ducks in a row, and the resources from the Puppet input
|
||||
run before those from the mcl input.
|
||||
|
||||
**Note:** The names of the `noop` and the class must be identical after the
|
||||
respective prefix. The common part (here, `handover_to_mgmt`) becomes the name
|
||||
of the merged resource.
|
||||
|
||||
## Mixed graph example 3 - Multiple merges
|
||||
|
||||
In most scenarios, it will not be possible to define a single handover
|
||||
point like in the previous example. For example, if some Puppet resources
|
||||
need to run in between two stages of native resources, you need at least
|
||||
two merged vertices:
|
||||
|
||||
```mcl
|
||||
# lang
|
||||
noop "puppet_handover" {}
|
||||
noop "puppet_handback" {}
|
||||
file "/tmp/mgmt_dir/" { state => "present" }
|
||||
file "/tmp/mgmt_dir/a" { state => "present" }
|
||||
file "/tmp/mgmt_dir/puppet_subtree/state-file" { state => "present" }
|
||||
|
||||
File["/tmp/mgmt_dir/"] -> Noop["puppet_handover"]
|
||||
Noop["puppet_handback"] -> File["/tmp/mgmt_dir/puppet_subtree/state-file"]
|
||||
```
|
||||
|
||||
```puppet
|
||||
# puppet
|
||||
class mgmt_handover {}
|
||||
class mgmt_handback {}
|
||||
|
||||
include mgmt_handover, mgmt_handback
|
||||
|
||||
class important_stuff {
|
||||
file { "/tmp/mgmt_dir/puppet_subtree":
|
||||
ensure => "directory"
|
||||
}
|
||||
# ...
|
||||
}
|
||||
|
||||
Class["mgmt_handover"] -> Class["important_stuff"] -> Class["mgmt_handback"]
|
||||
```
|
||||
|
||||
The resulting graph looks roughly like this:
|
||||
|
||||
```
|
||||
File[/tmp/mgmt_dir/] -> File[/tmp/mgmt_dir/a]
|
||||
|
|
||||
V
|
||||
Noop[handover] -> ( class important_stuff resources )
|
||||
|
|
||||
V
|
||||
Noop[handback]
|
||||
|
|
||||
V
|
||||
File[/tmp/mgmt_dir/puppet_subtree/state-file]
|
||||
```
|
||||
|
||||
You can add arbitrary numbers of merge pairs to your code bases,
|
||||
with relationships as needed. From our limited experience, code
|
||||
readability suffers quite a lot from these, however. We advise
|
||||
to keep these structures simple.
|
||||
127
docs/quick-start-guide.md
Normal file
127
docs/quick-start-guide.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Quick start guide
|
||||
|
||||
## Introduction
|
||||
|
||||
This guide is intended for users and developers. If you're brand new to `mgmt`,
|
||||
it's probably a good idea to start by reading an
|
||||
[introductory article about the engine](https://purpleidea.com/blog/2016/01/18/next-generation-configuration-mgmt/)
|
||||
and an [introductory article about the language](https://purpleidea.com/blog/2018/02/05/mgmt-configuration-language/).
|
||||
[There are other articles and videos available](on-the-web.md) if you'd like to
|
||||
learn more or prefer different formats. Once you're familiar with the general
|
||||
idea, or if you prefer a hands-on approach, please start hacking...
|
||||
|
||||
## Getting mgmt
|
||||
|
||||
You can either build `mgmt` from source, or you can download a pre-built
|
||||
release. There are also some distro repositories available, but they may not be
|
||||
up to date. A pre-built release is the fastest option if there's one that's
|
||||
available for your platform. If you are developing or testing a new patch to
|
||||
`mgmt`, or there is not a release available for your platform, then you'll have
|
||||
to build your own.
|
||||
|
||||
### Downloading a pre-built release:
|
||||
|
||||
The latest releases can be found [here](https://github.com/purpleidea/mgmt/releases/).
|
||||
An alternate mirror is available [here](https://dl.fedoraproject.org/pub/alt/purpleidea/mgmt/releases/).
|
||||
|
||||
Make sure to verify the signatures of all packages before you use them. The
|
||||
signing key can be downloaded from [https://purpleidea.com/contact/#pgp-key](https://purpleidea.com/contact/#pgp-key)
|
||||
to verify the release.
|
||||
|
||||
If you've decided to install a pre-build release, you can skip to the
|
||||
[Running mgmt](#running-mgmt) section below!
|
||||
|
||||
### Building a release:
|
||||
|
||||
You'll need some dependencies, including `golang`, and some associated tools.
|
||||
|
||||
#### Installing golang
|
||||
|
||||
* You need a modern golang version installed.
|
||||
* To install on rpm style systems: `sudo dnf install golang`
|
||||
* To install on apt style systems: `sudo apt install golang`
|
||||
* To install on macOS systems install [Homebrew](https://brew.sh)
|
||||
and run: `brew install go`
|
||||
* You can run `go version` to check the golang version.
|
||||
* If your distro is too old, you may need to [download](https://golang.org/dl/)
|
||||
a newer golang version.
|
||||
|
||||
#### Setting up golang
|
||||
|
||||
* You can skip this step, as your installation will default to using `~/go/`,
|
||||
but if you do not have a `GOPATH` yet and want one in a custom location, create
|
||||
one and export it:
|
||||
|
||||
```shell
|
||||
mkdir $HOME/gopath
|
||||
export GOPATH=$HOME/gopath
|
||||
```
|
||||
|
||||
* You might also want to add the GOPATH to your `~/.bashrc` or `~/.profile`.
|
||||
* For more information you can read the
|
||||
[GOPATH documentation](https://golang.org/cmd/go/#hdr-GOPATH_environment_variable).
|
||||
|
||||
#### Getting the mgmt code and associated dependencies
|
||||
|
||||
* Download the `mgmt` code and switch to that directory:
|
||||
|
||||
```shell
|
||||
git clone --recursive https://github.com/purpleidea/mgmt/ ~/mgmt/
|
||||
cd ~/mgmt/
|
||||
```
|
||||
|
||||
* Add `$GOPATH/bin` to `$PATH`
|
||||
|
||||
```shell
|
||||
export PATH=$PATH:$GOPATH/bin
|
||||
```
|
||||
|
||||
* Run `make deps` to install system and golang dependencies. Take a look at
|
||||
`misc/make-deps.sh` if you want to see the details of what it does.
|
||||
|
||||
#### Building mgmt
|
||||
|
||||
* Now run `make` to get a freshly built `mgmt` binary. If this succeeds, you can
|
||||
proceed to the [Running mgmt](#running-mgmt) section below!
|
||||
|
||||
### Installing a distro release
|
||||
|
||||
Installation of `mgmt` from distribution packages currently needs improvement.
|
||||
They are not always up-to-date with git master and as such are not recommended.
|
||||
At the moment we have:
|
||||
* [COPR](https://copr.fedoraproject.org/coprs/purpleidea/mgmt/) (currently dead)
|
||||
* [Arch](https://aur.archlinux.org/packages/mgmt/) (currently stale)
|
||||
|
||||
Please contribute more and help improve these! We'd especially like to see a
|
||||
Debian package!
|
||||
|
||||
### Building from a container:
|
||||
|
||||
This method avoids polluting your workstation with the dependencies for the
|
||||
build. Here is an example using Fedora, Podman and Buildah:
|
||||
|
||||
```shell
|
||||
git clone --recursive https://github.com/purpleidea/mgmt/
|
||||
cd mgmt
|
||||
docker build -t mgmt -f docker/Dockerfile .
|
||||
docker run --rm --entrypoint cat mgmt mgmt > mgmt
|
||||
chmod +x mgmt
|
||||
./mgmt --version
|
||||
# you could now copy the mgmt binary somewhere into your $PATH
|
||||
# e.g., /usr/local/bin/ to make it accessible from anywhere
|
||||
```
|
||||
|
||||
## Running mgmt
|
||||
|
||||
* Run `mgmt run --tmp-prefix lang examples/lang/hello0.mcl` to try out a very
|
||||
simple example! If you built it from source, you'll need to use `./mgmt` from
|
||||
the project directory.
|
||||
* Look in that example file that you ran to see if you can figure out what it
|
||||
did! You can press `^C` to exit `mgmt`.
|
||||
* Have fun hacking on our future technology and get involved to shape the
|
||||
project!
|
||||
|
||||
## Examples
|
||||
|
||||
Please look in the [examples/lang/](../examples/lang/) folder for some more
|
||||
examples!
|
||||
51
docs/release-notes/0.0.10
Normal file
51
docs/release-notes/0.0.10
Normal file
@@ -0,0 +1,51 @@
|
||||
I've just released version 0.0.10 of mgmt!
|
||||
|
||||
NEWS
|
||||
|
||||
57 files changed, 1991 insertions(+), 752 deletions(-)
|
||||
|
||||
* There's a new resource called `KV`. Short examples exist, but I
|
||||
haven't yet published a whole integration showing the usefulness.
|
||||
|
||||
* A major race was fixed. The issue of what to do with BackPokes during
|
||||
start/pause was never previously solved. I had this as an open issue on
|
||||
my whiteboard for a while, and I finally got some time to work through
|
||||
it. The answer wasn't that difficult, but I think it was shrouded in
|
||||
some tunnel vision. See the commit messages and source comments for the
|
||||
details.
|
||||
|
||||
* The GAPI grew four new associated World API methods: StrWatch,
|
||||
StrGet, StrSet, StrDel, and the associated etcd backed implementations.
|
||||
These are quite useful when combined with the KV resource.
|
||||
|
||||
* There are now P/V style counting semaphores available as metaparams.
|
||||
This is particularly cool because the implementation is (AFAIK,
|
||||
assuming no bugs) dead-lock free! This is mentioned in my recent blog
|
||||
post.
|
||||
|
||||
* See the git log for more NEWS, and sorry for anything notable I left
|
||||
out!
|
||||
|
||||
BUGS
|
||||
|
||||
* There's a `concurrent map write` bug in the semaphore implementation
|
||||
which is fixed in git master. Since it was a race, it was only caught
|
||||
after this release was made. I should also figure out if the sema check
|
||||
should go after the BackPoke or not.
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started!
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
James Shubin, Julien Pivotto, Michael Borden.
|
||||
We had 3 unique committers since 0.0.9, and have had 30 overall.
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
59
docs/release-notes/0.0.11
Normal file
59
docs/release-notes/0.0.11
Normal file
@@ -0,0 +1,59 @@
|
||||
I've just released version 0.0.11 of mgmt!
|
||||
|
||||
NEWS
|
||||
|
||||
20 files changed, 579 insertions(+), 126 deletions(-)
|
||||
|
||||
* Added a missing mutex around the semaphore map which prevents
|
||||
occasional panics
|
||||
|
||||
* Removed exec pollint param which is not needed because of the poll
|
||||
metaparam
|
||||
|
||||
* Fixed a state rechecking bug in exec resource (things are faster now)
|
||||
|
||||
* Fixed the major annoyance of exec resources receiving main's signals.
|
||||
If we would ^C the main mgmt, the child processes would receive this
|
||||
too which was incorrect.
|
||||
|
||||
* Fixed the deadlock on resource errors. This meant that previously if
|
||||
a resource failed, we would deadlock the graph from shutting down. This
|
||||
was bad and I'm glad it's now fixed. Sorry about that!
|
||||
|
||||
* Improved the backpoke logic to not require semaphores since we used
|
||||
to take the lock even when we were going to backpoke which was
|
||||
unnecessary.
|
||||
|
||||
* Added fast pausing to the graph. This means that a ^C or a pause
|
||||
transition used to wait for the whole graph to roll through, but it now
|
||||
finishes after the currently running resources finish executing. Read
|
||||
the commit messages for more background here including the discussion
|
||||
about a possible Interrupt() addition to the resource API.
|
||||
|
||||
* Prometheus support has been updated!
|
||||
|
||||
* See the git log for more NEWS, and sorry for anything notable I left
|
||||
out!
|
||||
|
||||
BUGS
|
||||
|
||||
* We're in pretty good shape now. There are some small issues, but
|
||||
nothing major that I don't know about.
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started!
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
James Shubin, Julien Pivotto
|
||||
We had 2 unique committers since 0.0.10, and have had 30 overall.
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
87
docs/release-notes/0.0.12
Normal file
87
docs/release-notes/0.0.12
Normal file
@@ -0,0 +1,87 @@
|
||||
I've just released version 0.0.12 of mgmt!
|
||||
|
||||
Sorry if I've been more quiet than usual, I've had to focus a lot of my
|
||||
time on GlusterFS related features ($dayjob official directives) and
|
||||
work there.
|
||||
|
||||
One goal is to try and use libmgmt to either wholly or partially re-
|
||||
implement glusterd. As a result, a lot of my upstream focus has been
|
||||
re-prioritized to features needed for that effort.
|
||||
|
||||
I wrote a PoC called gd3: https://github.com/purpleidea/gd3
|
||||
(It has now bit-rotted compared to upstream mgmt, but is easy to fix.)
|
||||
The initial scope looks like it will be much smaller, but hopefully
|
||||
this is interesting to you too. Ping me if you'd like to help.
|
||||
|
||||
We desperately need your contributions if we're going to get mgmt
|
||||
standalone into a MVP. To motivate you, there's some great new stuff
|
||||
that landed since 0.0.11, including:
|
||||
|
||||
* a great new YAML parser from contributor Mildred
|
||||
|
||||
* a huge pgraph refactor (to remove internal deps and cycles)
|
||||
|
||||
* a great amount of new tests and testing
|
||||
|
||||
and so much more...
|
||||
|
||||
NEWS
|
||||
|
||||
76 files changed, 7549 insertions(+), 4269 deletions(-)
|
||||
|
||||
* The svc resource now also supports user services
|
||||
|
||||
* There's a fabulous new yaml parser that fixes a longstanding issue in
|
||||
my original code. Try it with --yaml2. I'll remove the old one
|
||||
eventually. Thanks to contributor Mildred for this great work!
|
||||
|
||||
* Refactored the lib/ etcd usage into the GAPI's for a cleaner main
|
||||
|
||||
* World API grew some new methods for libmgmt users
|
||||
|
||||
* pgraph refactor and cleanup, now it's a fairly clean standalone pkg
|
||||
|
||||
* pgraph functions to flatten/merge in subgraphs along with examples
|
||||
|
||||
* Giant resource refactor to hopefully make things more logical, and to
|
||||
simplify the resource API. This also introduces the mgraph struct to
|
||||
add the higher level graph knowledge outside of pgraph.
|
||||
|
||||
* A partial implementation of a "Graph" (recursive subgraph?) resource!
|
||||
See the code for details, as help is wanted to finish this. This should
|
||||
help elucidate what the most elegant design for the mgmt core should
|
||||
be.
|
||||
|
||||
* Send/Recv support for the exec resource as output, stdout, and stderr
|
||||
|
||||
* GAPI improvements to support exit requests and fast pausing
|
||||
|
||||
* AutoEdge API improvements including a fix+test for a regression
|
||||
|
||||
* A possible fix for the possible etcd server startup race
|
||||
|
||||
* A fun amount of new tests all over including for gometalinter
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
BUGS
|
||||
|
||||
* We fixed a bunch of stuff, and added more tests!
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started!
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
James Shubin, Julien Pivotto, Mildred Ki'Lya
|
||||
We had 3 unique committers since 0.0.11, and have had 30 overall.
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
89
docs/release-notes/0.0.13
Normal file
89
docs/release-notes/0.0.13
Normal file
@@ -0,0 +1,89 @@
|
||||
I've just released version 0.0.13 of mgmt!
|
||||
|
||||
I guess this is an appropriate number for a scary October release.
|
||||
|
||||
We recently re-licensed to the more permissive GPL. I put a lot of
|
||||
thought into it, and also wrote up a short blog post about some of the
|
||||
reasoning. It's here:
|
||||
|
||||
https://ttboj.wordpress.com/2017/10/17/copyleft-is-dead-long-live-copyl
|
||||
eft/
|
||||
|
||||
If you read to the end you'll also see that Red Hat is not funding my
|
||||
mgmt work anymore. This is too bad, and means I'll only have small
|
||||
amounts of personal time available to work on this. If you'd like to
|
||||
help fund my work, or know someone who'd like to, please let me know!
|
||||
|
||||
Having said that, there's some great new stuff that landed since
|
||||
0.0.12, including:
|
||||
|
||||
* new resources from new contributor Jonathan Gold (aws, group, user)
|
||||
|
||||
* an HCL frontend from new contributor Chris McKenzie
|
||||
|
||||
* polish in a number of places including in the nspawn resource
|
||||
|
||||
and so much more... If you'd prefer to have releases more often, then
|
||||
please let me know! Lastly, the language and a number of associated
|
||||
parts are on its way. I hope to push this monster patch to git master
|
||||
before February.
|
||||
|
||||
It's also worth mentioning that we have 17 resources now! wow.
|
||||
|
||||
NEWS
|
||||
|
||||
140 files changed, 3921 insertions(+), 848 deletions(-)
|
||||
|
||||
* Many improvements to tests, testing and small fixes to avoid false-
|
||||
failures on travis.
|
||||
|
||||
* golint now reports 0 issues.
|
||||
|
||||
* An HCL frontend if you'd prefer that to the YAML. Also a great
|
||||
example of how to plug in a new frontend.
|
||||
|
||||
* An update to golang 1.8 as the minimum version required.
|
||||
|
||||
* Bump the etcd version to 3.2.6+ -- Looking forward to a 3.3 release
|
||||
which should probably include some patches we upstreamed.
|
||||
|
||||
* Addition of new user and group resources. These also include a bunch
|
||||
of automatic edges.
|
||||
|
||||
* Addition of an AWS resource! I've wanted this for a while, as it
|
||||
demonstrates nicely how event based cloud resources can fit nicely into
|
||||
our design. There's still a lot to do, and we have some suggestions for
|
||||
Amazon too. If you have a contact there, please put me in touch!
|
||||
|
||||
* Our nspawn resource is more polished now.
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started!
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is free and friendly. You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
AdnanLFC, Arthur Mello, ChrisMcKenzie, Dennis Kliban, Ismael Puerto,
|
||||
James Shubin, Jonathan Gold, Juan Luis de Sousa-Valadas Castaño, Juan-
|
||||
Luis de Sousa-Valadas Castaño (although I suspect the last two are the
|
||||
same ;))
|
||||
|
||||
We had 9 unique committers since 0.0.12, and have had 38 overall.
|
||||
Run 'git log 0.0.12..0.0.13' to see what has changed since 0.0.12
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
81
docs/release-notes/0.0.14
Normal file
81
docs/release-notes/0.0.14
Normal file
@@ -0,0 +1,81 @@
|
||||
I've just released version 0.0.14 of mgmt!
|
||||
|
||||
> 118 files changed, 2688 insertions(+), 974 deletions(-)
|
||||
|
||||
There's some great new stuff that landed since
|
||||
0.0.13, including:
|
||||
|
||||
* amazon AWS EC2 resource is now in git master.
|
||||
|
||||
* more automatic edges from new contributor Guillaume Herail (xiu)
|
||||
|
||||
* a move to golang 1.8 or higher
|
||||
|
||||
and so much more... This will probably be the last release before the
|
||||
language lands in git master. It's a pretty giant patch coming :/
|
||||
|
||||
NEWS
|
||||
|
||||
* We're > 1k stars on GitHub now. It's a silly metric, but ¯\_(ツ)_/¯
|
||||
|
||||
* Jonathan Gold has done a lot of hard work on the AWS EC2 resource,
|
||||
and it's now in git master. There are still many things we'd like to
|
||||
do, but it's a great start on what is a *monster* resource, and
|
||||
hopefully it will inspire others to get involved.
|
||||
|
||||
In particular, it was a great learning experience (for me in
|
||||
particular!) about how bad the EC2 golang API is. There are some
|
||||
notable design bugs we found, and if anyone from Amazon engineering
|
||||
would like to reach out to us, we'd love to provide some constructive
|
||||
ideas.
|
||||
|
||||
* Guillaume Herail (xiu) wrote some really nice patches, and picked up
|
||||
on the autoedges API very quickly. Hopefully he'll have time to work on
|
||||
even more!
|
||||
|
||||
* Paul Morgan sent us some nice shell fixups-- many more exist in an
|
||||
open PR that didn't make it into this release. Hopefully we'll get
|
||||
those merged by 0.0.15
|
||||
|
||||
* Felix Frank did a few Puppet compiler fix ups. I think he's been
|
||||
refreshing his work with new resources recently...
|
||||
|
||||
* We're looking for help writing Google, DigitalOcean, Rackspace, etc
|
||||
resources if anyone is interested, reach out to us. Particularly if
|
||||
there is support from those organizations as well.
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
EVENTS
|
||||
|
||||
There are a bunch of upcoming mgmt talks and events! Stay tuned for
|
||||
details in the coming email, but TL;DR: Linux Conf Australia, FOSDEM, &
|
||||
CfgMgmtCamp.eu -- from three different speakers, and including a
|
||||
hackathon too!
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started! Expect many new tagged #mgmtlove
|
||||
issues within the month.
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is free and friendly. You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
Felix Frank, Guillaume Herail, James Shubin, Jonathan Gold,
|
||||
jonathangold, Julien Pivotto, Paul Morgan, Toshaan Bharvani
|
||||
We had 8 unique committers since 0.0.13, and have had 41 overall.
|
||||
run 'git log 0.0.13..0.0.14' to see what has changed since 0.0.13
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
116
docs/release-notes/0.0.15
Normal file
116
docs/release-notes/0.0.15
Normal file
@@ -0,0 +1,116 @@
|
||||
I've just released version 0.0.15 of mgmt!
|
||||
|
||||
> 328 files changed, 29869 insertions(+), 943 deletions(-)
|
||||
|
||||
(Yeah, that's almost 30k+ LOC)
|
||||
|
||||
There's some great new stuff that landed since 0.0.14, including:
|
||||
|
||||
* THE LANGUAGE (mcl)
|
||||
|
||||
* "Deploys": a distributed way to push code into your cluster
|
||||
|
||||
* Scheduling (as a reactive function)
|
||||
|
||||
* Better testing
|
||||
|
||||
* a move to etcd 3.3+ and golang 1.9+
|
||||
|
||||
and so much more... This is a monster release. Please try out the
|
||||
language and all the other new features today :)
|
||||
|
||||
NEWS
|
||||
|
||||
* We released the language. Please play around with it :) It's time to
|
||||
get used to this cool new paradigm. Learn more from the...
|
||||
|
||||
Blog post:
|
||||
https://purpleidea.com/blog/2018/02/05/mgmt-configuration-language/
|
||||
|
||||
Video:
|
||||
https://www.youtube.com/watch?v=NxObmwZDyrI
|
||||
|
||||
Docs:
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/language-guide.md
|
||||
|
||||
Function guide:
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/function-guide.md
|
||||
|
||||
And tons of code all over git master. Check the lang/ folder.
|
||||
|
||||
* There is a reactive scheduler in the language. Use your imagination,
|
||||
or play around with:
|
||||
https://github.com/purpleidea/mgmt/blob/3ad7097c8aa7eab7f895aab9af22338
|
||||
c0cf82986/lang/funcs/core/schedule_polyfunc.go#L18
|
||||
|
||||
* There is a "deploys" feature. It's not documented yet. You should
|
||||
poke around if you're curious. Consider this an early soft release.
|
||||
|
||||
* There is a FS implementation to store files in a POSIX-like layer on
|
||||
top of etcd. It's used by deploys. It needs more tests though :)
|
||||
|
||||
* The language grew two "simple" API's for implementing functions, so
|
||||
that new functionality can be exposed in the mgmt language.
|
||||
|
||||
* The language grew two ways to specify edges between resources: either
|
||||
internal to the resource, or externally as standalone edges.
|
||||
|
||||
* The language now supports optional resource parameters via the
|
||||
"elvis" operator. This keeps things type safe and avoids needing an
|
||||
"undef" or "nil" in the language. This operator also works for edge
|
||||
declarations.
|
||||
|
||||
* New contributor Johan Bloemberg has been on fire sending patches!
|
||||
He has already made some great improvements to our Makefile for
|
||||
testing, and the addition of the env* functions in the language, with
|
||||
much more code pending in open PR's.
|
||||
|
||||
* Initial debian packaging has been added. It now needs a maintainer to
|
||||
build, upload, and love it :)
|
||||
|
||||
* We have an early emacs major mode for "mcl", our language.
|
||||
|
||||
* Lots of new documentation has been added. Particularly for developers
|
||||
wanting to contribute to the project.
|
||||
|
||||
* We're looking for help writing Google, DigitalOcean, Rackspace, etc
|
||||
resources if anyone is interested, reach out to us. Particularly if
|
||||
there is support from those organizations as well.
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many new tagged #mgmtlove issues were tagged:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved.
|
||||
This is free and friendly. You get to improve your skills,
|
||||
and we get
|
||||
some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
Carsten Thiel, dsx, James Shubin, Joe Julian, Johan Bloemberg, Jonathan
|
||||
Gold, jonathangold, karimb, Oliver Frommel, Peter Oliver, Toshaan
|
||||
Bharvani, Wim
|
||||
We had 12 unique committers since 0.0.14, and have had 48 overall.
|
||||
run 'git log 0.0.14..0.0.15' to see what has changed since 0.0.14
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
104
docs/release-notes/0.0.16
Normal file
104
docs/release-notes/0.0.16
Normal file
@@ -0,0 +1,104 @@
|
||||
I've just released version 0.0.16 of mgmt!
|
||||
|
||||
> 220 files changed, 14243 insertions(+), 9491 deletions(-)
|
||||
|
||||
Woo...
|
||||
|
||||
There's some great new stuff that landed since 0.0.15, including:
|
||||
|
||||
* A giant engine re-write! (Makes resource writing more elegant too.)
|
||||
|
||||
* New resources!!
|
||||
|
||||
* New language features!!!
|
||||
|
||||
and so much more... This is a monster release. Please try out the new
|
||||
features today :)
|
||||
|
||||
NEWS
|
||||
|
||||
* New resources include: net, mount and docker:container. Jonathan was
|
||||
responsible for all of these. Please take them for a spin! He's looking
|
||||
for a job too, and would probably be happy to get paid to work on mgmt.
|
||||
|
||||
* We're > 1.5k stars on GitHub now. It's a silly metric, but ¯\_(ツ)_/¯
|
||||
|
||||
* A giant engine refactoring/re-write was done. This cleaned up the
|
||||
code significantly, and made it more elegant to write resources.
|
||||
Unfortunately there is one small bug that I missed and that I haven't
|
||||
fixed yet. It rarely happens except during some of our tests during
|
||||
shutdown, which causes intermittent failures. It shouldn't block you
|
||||
playing with mgmt.
|
||||
|
||||
* The language "class" and "include" statements have been added. These
|
||||
are important pieces for writing reusable modules which are coming
|
||||
soon. Try them out! (This comes with a bunch of tests too.)
|
||||
|
||||
* We have an integration testing framework. It's pretty cool, it spins
|
||||
up a full mgmt cluster and runs stuff. Try it out or add some tests.
|
||||
|
||||
* I had fun fixing a big bug: 06ee05026b0c743d19c7d62675f8ddeabdc8dd4f
|
||||
|
||||
* I removed the remote execution functionality from core. I realized it
|
||||
could be re-written as a resource, and it was also in the way from some
|
||||
other cleanups that were more important. Half the new code is done,
|
||||
ping me if this is a priority for you or you want to help.
|
||||
|
||||
* I also removed the HCL front-end, because mcl is usable enough to be
|
||||
more fun to play with, and I wanted to refactor some code. If someone
|
||||
really wants it back, let me know.
|
||||
|
||||
* We have some release building scripts in git master, so you can now
|
||||
download pre-built (with fpm) RPM, DEB, or PACMAN packages! They're
|
||||
signed too. https://github.com/purpleidea/mgmt/releases/tag/0.0.16
|
||||
|
||||
* We're looking for help writing Google, DigitalOcean, Rackspace, etc
|
||||
resources if anyone is interested, reach out to us. Particularly if
|
||||
there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
MISC
|
||||
|
||||
I took a bit of a break recently to catch up on some life stuff, but I
|
||||
think I'm back on track. While git master hasn't been especially busy,
|
||||
there's an active feature branch at feat/import which contains some fun
|
||||
stuff, with a very WIP giant patch still sitting on my machine. I hope
|
||||
to finish it up soon and then do another release. That branch contains
|
||||
one of the last big features before I'll really be ready to run mgmt on
|
||||
my personal servers!
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many new tagged #mgmtlove issues were tagged:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved.
|
||||
This is free and friendly. You get to improve your skills,
|
||||
and we get
|
||||
some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
Alan Jenkins, James Shubin, jesus m. rodriguez, Jonathan Gold,
|
||||
jonathangold, Lauri Ojansivu, phaer
|
||||
We had 7 unique committers since 0.0.15, and have had 52 overall.
|
||||
run 'git log 0.0.15..0.0.16' to see what has changed since 0.0.15
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
111
docs/release-notes/0.0.17
Normal file
111
docs/release-notes/0.0.17
Normal file
@@ -0,0 +1,111 @@
|
||||
I've just released version 0.0.17 of mgmt!
|
||||
|
||||
> 269 files changed, 13281 insertions(+), 1633 deletions(-)
|
||||
|
||||
There's some very useful stuff that landed since 0.0.16, including:
|
||||
|
||||
* Modules and import system now exists!
|
||||
* A lot of tests, fixes and a huge new test infra was added
|
||||
* Merging puppet with mcl code is now possible (madness!)
|
||||
* We made a small change to the cli UI
|
||||
* Bump to golang 1.10 (please update your environments)
|
||||
|
||||
And much more...
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are available here:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.17
|
||||
|
||||
NEWS
|
||||
|
||||
* One of the biggest missing features was the lack of a module/import
|
||||
system. After some initial exploration on what turned out to be a dead-
|
||||
end, I found what I think is a very elegant approach, which is now in
|
||||
this release. Please try it out, there are docs available. I hope to
|
||||
write a blog post about it soon.
|
||||
|
||||
(There's one additional "kind" of import that I'm considering, similar
|
||||
to a macro "#include", that I might add. To be determined. Let me know
|
||||
if you find anything missing as of today.)
|
||||
|
||||
* Felix added the first version of his mcl+puppet frontend. This should
|
||||
allow you to more cleverly merge legacy puppet environments with mcl
|
||||
code. It's fantastic, take a look.
|
||||
|
||||
* Jonathan added a systemd-timer resource. This is a great replacement
|
||||
for cron.
|
||||
|
||||
* We changed the CLI ui to improve the determinism of the frontend
|
||||
selection. Basically we changed from: `mgmt run --lang code.mcl` to:
|
||||
`mgmt run lang --lang code.mcl`. Remember to put --tmp-prefix after
|
||||
`run` directly where it is used.
|
||||
|
||||
* We made a whole bunch of cleanups to the test infra, added new test
|
||||
infra for testing complex mcl modules and the import/module system, and
|
||||
of course added new tests.
|
||||
|
||||
* You can pass a list of strings as the resource name to build that
|
||||
many resources. (Looping/iteration!)
|
||||
|
||||
* You can specify all the metaparams and auto-* properties in mcl now.
|
||||
|
||||
* Native mcl code can be used to write imported core code.
|
||||
|
||||
* There was a bug that snuck into the pkg res. This has now been fixed.
|
||||
|
||||
* A small, long-time copy+pasta error bug was fixed in Exec.
|
||||
|
||||
* Virtually all the imports of the "log" package are at the top-level
|
||||
now. This will make moving to a new logger easier in the future. I have
|
||||
an innovative logger idea that I have a design for that I'll eventually
|
||||
get to.
|
||||
|
||||
* A few workarounds for occasional test failures were added. Some
|
||||
legacy code needs a cleanup, and it's not done yet. Fortunately, none
|
||||
of these issues seem to occur in real-life as far as I can tell, and
|
||||
are caused by closing down mgmt at weird times.
|
||||
|
||||
* Found a bug (now fixed) in the upstream lexer Yikes! See:
|
||||
57ce3fa587897d74634c1216af67dd42252c64e5
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, etc
|
||||
resources if anyone is interested, reach out to us. Particularly if
|
||||
there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many new tagged #mgmtlove issues were tagged:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is fun and friendly! You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
Felix Frank, James Shubin, Jonathan Gold, Kevin Kuehler, Michael Lesko-
|
||||
Krleza, Tom Payne, Vincent Membré
|
||||
We had 7 unique committers since 0.0.16, and have had 56 overall.
|
||||
run 'git log 0.0.16..0.0.17' to see what has changed since 0.0.16
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
106
docs/release-notes/0.0.18
Normal file
106
docs/release-notes/0.0.18
Normal file
@@ -0,0 +1,106 @@
|
||||
I've just released version 0.0.18 of mgmt!
|
||||
|
||||
> 202 files changed, 5606 insertions(+), 1880 deletions(-)
|
||||
|
||||
There's some great stuff that landed since 0.0.17, including:
|
||||
|
||||
* A re-write of the core engine algorithm
|
||||
* Tests are very stable
|
||||
* At least three old bugs were killed
|
||||
* An infra to write tests for individual resources was added
|
||||
|
||||
And much more...
|
||||
|
||||
This adds a significant amount of polish and bug fixes to mgmt. We're
|
||||
on the home stretch to MVP!
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are available here:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.18
|
||||
|
||||
NEWS
|
||||
|
||||
* There was a rare race that would panic the engine on shutdown. This
|
||||
only seemed to happen in CPU/system call starved environments like
|
||||
travis-ci. This was due to some ignorance when writing this early part
|
||||
of the code base. The algorithm has been re-written, and this also
|
||||
removed the use of at least one mutex. Things should be stable now, and
|
||||
also much faster. (Although perf was never an issue.)
|
||||
|
||||
* A hidden race/deadlock in the exec resource was found and killed.
|
||||
Woo! Some new tools to help find these and other problems are in misc/
|
||||
|
||||
* The early converger code was re-written. I was not as knowledgeable
|
||||
about golang in the very beginning, and this code needed refreshing. It
|
||||
contained a rare deadlock which needed to be killed.
|
||||
|
||||
* Toshaan added an uptime() function.
|
||||
|
||||
* Julien added a method for generating some simple functions for the
|
||||
language.
|
||||
|
||||
* Lander added two new functions.
|
||||
|
||||
* James added a new readfile() function, and other examples.
|
||||
|
||||
* The template function now allows you to use imported functions. They
|
||||
use underscores instead of periods for namespace separation due to a
|
||||
limitation in the template library.
|
||||
|
||||
* Kevin and I killed a tricky race in the SocketSet code! Woo :) Kevin
|
||||
also added a great cpucount() fact!
|
||||
|
||||
* James gave a number of presentations at FOSDEM. Some recordings are
|
||||
available: https://purpleidea.com/talks/
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, etc
|
||||
resources if anyone is interested, reach out to us. Particularly if
|
||||
there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
BUGS
|
||||
|
||||
* An unfortunate bug in the type unification code was found. This can
|
||||
cause small code bases to take a lot of ram/cpu to run. This will be
|
||||
prioritized in an upcoming release. Until then you'll have to avoid
|
||||
fancy type unification. (Specify types you know when it has to guess.)
|
||||
|
||||
If efficient type unification algorithms are your specialty, please let
|
||||
us know, we'd like your help!
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is fun and friendly! You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
Felix Frank, James Shubin, Jeff Waugh, Johan Bloemberg, Julien Pivotto,
|
||||
Kevin Kuehler, Lander Van den Bulcke, Toshaan Bharvani
|
||||
We had 8 unique committers since 0.0.17, and have had 58 overall.
|
||||
run 'git log 0.0.17..0.0.18' to see what has changed since 0.0.17
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
131
docs/release-notes/0.0.19
Normal file
131
docs/release-notes/0.0.19
Normal file
@@ -0,0 +1,131 @@
|
||||
I've just released version 0.0.19 of mgmt!
|
||||
|
||||
> 361 files changed, 10451 insertions(+), 3919 deletions(-)
|
||||
|
||||
This is a very important (and huge) release and has some important
|
||||
fixes that landed since 0.0.18, including:
|
||||
|
||||
* A huge re-write of the elastic etcd clustering code base
|
||||
* A significant improvement in the type unification algorithm
|
||||
* An important import/class scoping bug was discovered and fixed
|
||||
* New mcl functions and resource improvements
|
||||
|
||||
And much more...
|
||||
|
||||
What comes next is just polish, new features and small bug fixes!
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are available here:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.19
|
||||
|
||||
NEWS
|
||||
|
||||
* A giant etcd re-write was completed and merged. The elastic
|
||||
clustering algorithm is not perfect, however it should suffice for most
|
||||
use cases, and it's always possible to point mgmt at an external etcd
|
||||
cluster if you don't understand the limitations of the automatic
|
||||
clustering algorithm. The important part is that the core code is much
|
||||
cleaner now, so hopefully races and bugs of ignorance are gone now. :)
|
||||
|
||||
* I found an unfortunate bug in the type unification algorithm that
|
||||
severely impacted performance for some types of code bases. This is now
|
||||
fixed, and I hope we should not experience problems again! Special
|
||||
thanks to Sam for talking me through the problem and understanding the
|
||||
space better! Woo \o/
|
||||
|
||||
* An important import/class scoping bug was fixed. Thanks to Nicolas
|
||||
for the bug report. We also added tests for this too!
|
||||
|
||||
* Nicolas also added our first os detection function. os.is_debian and
|
||||
os.is_redhat are now in core. Get your favourite os added today!
|
||||
|
||||
* The polymorphic len function can also check str length.
|
||||
|
||||
* The exec resource got a big cleanup. It also learned the interrupt
|
||||
trait so that long running commands can be forcefully killed if need
|
||||
be.
|
||||
|
||||
* A fancy new test infra for testing functions over time was added.
|
||||
Anytime we want to check our individual FRP functions are working as
|
||||
expected, this is an easy way to add a test. This way, if we ever find
|
||||
a bug, we can drop in a test with the fix. This actually helped find a
|
||||
very subtle bug in readfile that nobody had experienced yet!
|
||||
|
||||
* File res with state => exists but no content now performs as
|
||||
expected.
|
||||
|
||||
* Improved send/recv, since it was neglected a bit. Hopefully it ends
|
||||
up being a useful primitive.
|
||||
|
||||
* Added a new synchronization primitive that I'm called
|
||||
SubscribedSignal. I found it very useful for building some of my code,
|
||||
and I hope you'll find it useful too. I'd offer it upstream to the sync
|
||||
package if Google didn't force their crappy CLA nonsense on everyone
|
||||
who wanted to send a patch. :/ Death by 1000 paper cuts, I guess.
|
||||
|
||||
* Added a match function in the new regexp core package. Try it out and
|
||||
add some more functions!
|
||||
|
||||
* Wouter has been testing mgmt and filling all sorts of useful bug
|
||||
reports. We fixed at least one related to a report, and more are
|
||||
planned for the next release. Wouter also sent in one cleanup patch to
|
||||
remove some dead code. Welcome to the project!
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, etc
|
||||
resources if anyone is interested, reach out to us. Particularly if
|
||||
there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
BUGS
|
||||
|
||||
* There are a few known issues, in particular with some over eager
|
||||
checking done in the Validate portion of two resources, that should
|
||||
actually be runtime checks in CheckApply. As a result, if you intend to
|
||||
change some state during the graph execution, the resource won't see
|
||||
it. These should be very easy to fix if someone is interested in
|
||||
writing the patch!
|
||||
|
||||
TALKS
|
||||
|
||||
* James will be presenting at this year's OSDC in Berlin. There will be
|
||||
a workshop: https://osdc.de/events/mgmt-config-workshop/
|
||||
and a talk:
|
||||
https://osdc.de/events/mgmt-config-the-future-of-your-autonomous-datacentre/
|
||||
Sign up soon if you want to guarantee a spot, as they're limited!
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is fun and friendly! You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
Adam Sigal, Felix Frank, James Shubin, Jonathan Gold, Michael Schubert,
|
||||
Mitch Fossen, Nicolas Charles, Wouter Dullaert
|
||||
We had 8 unique committers since 0.0.18, and have had 63 overall.
|
||||
run 'git log 0.0.18..0.0.19' to see what has changed since 0.0.18
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
145
docs/release-notes/0.0.20
Normal file
145
docs/release-notes/0.0.20
Normal file
@@ -0,0 +1,145 @@
|
||||
I've just released version 0.0.20 of mgmt!
|
||||
|
||||
> 295 files changed, 8585 insertions(+), 1413 deletions(-)
|
||||
|
||||
This was a very challenging release but it includes many useful changes
|
||||
since 0.0.19, including:
|
||||
|
||||
* Function values / lambdas exist and are first-class
|
||||
* Over 70 new tests have been added!
|
||||
* Core functions, classes, and globals can now be written in mcl code
|
||||
* A new compiler "Ordering" step was added
|
||||
|
||||
And much more...
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are available here:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.20
|
||||
|
||||
NEWS
|
||||
|
||||
* After a long and challenging road, I finally got function values and
|
||||
lambdas merged. If you find any bugs, please let me know! You can now
|
||||
treat functions as first-class values, and even use them as closures by
|
||||
capturing variable state.
|
||||
|
||||
* Over 70 new tests were added, most of which test the behaviour of the
|
||||
new functions.
|
||||
|
||||
* Core packages always allowed you to write new functions in pure
|
||||
golang, but now you can implement new functions, classes, and even
|
||||
variables in native mcl code! It's still desirable and perhaps
|
||||
efficient in some cases to want native golang implementations, but all
|
||||
good programs self-host some of their stdlib in their own language
|
||||
eventually.
|
||||
|
||||
* A new compiler step called "Ordering" was added. It's hidden inside
|
||||
the SetScope step, but now lets us generate code ordering graphs and
|
||||
determine exactly what to run first.
|
||||
|
||||
* Light copying of Node's allows more correct and efficient function
|
||||
graphs that can share common vertices and edges. For example, if two
|
||||
different closures capture a variable $x, they'll both use the same
|
||||
copy when running the function, since the compiler can prove if they're
|
||||
identical.
|
||||
|
||||
* Improved the type system slightly to allow advanced type comparisons.
|
||||
|
||||
* The type unification algorithm was improved. Hopefully it should
|
||||
solve all scenarios without needing the recursive solver which was very
|
||||
slow. If you find a case that isn't speedy, please let us know!
|
||||
|
||||
* Added subtest listing by using -short and -v in a test. This lets you
|
||||
know what's available and how to run individual sub tests easily.
|
||||
|
||||
* Support for the systemd STATE_DIRECTORY and xdg cache dir was added
|
||||
by new contributor John! Thanks!
|
||||
|
||||
* New contributor Adam added a pgraph test.
|
||||
|
||||
* A bug in the systemd mount resource was fixed. Hopefully it should
|
||||
work correctly now.
|
||||
|
||||
* A bug that prevented us from allowing nested system imports was
|
||||
fixed. Nest away! This will pave the way for us to automatically import
|
||||
most of the golang standard library by doing: import "golang/regexp" or
|
||||
golang/whatever".
|
||||
|
||||
* Added an example showing that unicode is allowed in strings.
|
||||
|
||||
* Fixed a rare race in the engine.
|
||||
|
||||
* Added some new core functions including math.mod and datetime
|
||||
improvements.
|
||||
|
||||
* Changed the API to remove the use of --lang. This avoids the
|
||||
stuttering.
|
||||
|
||||
* Moved to golang 1.11 and etcd 3.3.13. The later includes a fix for an
|
||||
un-catchable error scenario which we fixed in etcd.
|
||||
|
||||
* Improved the pgraph library significantly so that we can generate
|
||||
better graphs with accurate vertices based on the vertex pointers.
|
||||
|
||||
* Added ArchLinux OS family detection.
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, etc
|
||||
resources if anyone is interested, reach out to us. Particularly if
|
||||
there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
BUGS
|
||||
|
||||
* There are a few known issues with some disabled code paths in the new
|
||||
function value code. These issues don't *need* to be fixed, but if they
|
||||
are, then we should see a slight performance increase. Happy to have
|
||||
someone dig into these, and they shouldn't bother anyone.
|
||||
|
||||
* Some of the pre-built binaries might not work on your system. We need
|
||||
to start building them with the right dependencies so that `ldd`
|
||||
related things are happy. For now, please try building yourself if the
|
||||
build doesn't work for you, or help improve our build system.
|
||||
|
||||
TALKS
|
||||
|
||||
I'll be giving a talk at an upcoming mini-conference in Montreal. If
|
||||
you're interested in attending, please let me know.
|
||||
If you'd like to give an mgmt talk somewhere, please let me know!
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is fun and friendly! You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
Adam Sigal, Christian Rebischke, Felix Frank, James Shubin, Jan
|
||||
Martens, Johan Bloemberg, John Hooks, Ward Vandewege
|
||||
We had 8 unique committers since 0.0.19, and have had 67 overall.
|
||||
run 'git log 0.0.19..0.0.20' to see what has changed since 0.0.19
|
||||
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
133
docs/release-notes/0.0.21
Normal file
133
docs/release-notes/0.0.21
Normal file
@@ -0,0 +1,133 @@
|
||||
I've just released version 0.0.21 of mgmt!
|
||||
|
||||
> 178 files changed, 4351 insertions(+), 829 deletions(-)
|
||||
|
||||
This was a very lonely release but it includes some very useful
|
||||
additions since 0.0.20, including:
|
||||
|
||||
* The first mgmt meme!
|
||||
* Working distro packages for Fedora, Debian, Ubuntu and Arch!
|
||||
* Reversible resources!
|
||||
* A deploy package to let you read files from the active deploy
|
||||
* Improved file resource behaviours
|
||||
|
||||
And much more...
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are available here:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.21
|
||||
|
||||
NEWS
|
||||
|
||||
* Someone made a cool mgmt meme. Look in art/mgmt_*_meme.jpg
|
||||
|
||||
* Distro packages are now built properly, so they should all work now.
|
||||
We have builds for Fedora-30, Fedora-29, Debian-10, Ubuntu-Bionic, and
|
||||
Archlinux. If you'd like a build for a different distro/version please
|
||||
let me know.
|
||||
|
||||
* We finally got rid of the old `Compare(...) bool` API, and moved to
|
||||
`Cmp(...) error`. We'll now get more useful information from Res
|
||||
compares when they differ. Thanks to new contributor Donald Bakong for
|
||||
working on this. He's ramping up his golang contributions, so we expect
|
||||
to see more from him in the future!
|
||||
|
||||
* We now have reversible resources. Basically if you create a resource
|
||||
and specify the reverse metaparam, eg: `Meta:reverse => true`, then if
|
||||
a resource is removed (either because a new version of code doesn't
|
||||
have it anymore OR more importantly if it was inside an `if` block that
|
||||
became false) then the engine will perform some "reverse" action for
|
||||
it. For a file, if it was added, we'll remove it. If it was edited,
|
||||
we'll undo the edit. If we added ugo+w, we'll remove that. And so on.
|
||||
The engine bits are done, and as well so have the file resource bits.
|
||||
It should be easy to add this for any other resource where it makes
|
||||
sense. This will likely be a very powerful feature that we use a lot.
|
||||
|
||||
* The file resource was changed slightly so that by default the "state"
|
||||
is undefined. As a result, if you want a file to be created and none is
|
||||
present, you need to specify the state. Otherwise specifying "content"
|
||||
will only edit a file if it already exists, and otherwise be an error.
|
||||
It turns out this is actually a better behaviour, even if it's not
|
||||
necessarily intuitive for puppet users. It turns out it simplifies the
|
||||
code drastically and makes the reversible file resource much more
|
||||
logical. It seems that Puppet and Ansible got this wrong, but Cfengine
|
||||
got it right. IIRC. Do you agree? (Look at the code!)
|
||||
|
||||
* We now catch CR \r characters in code so that you don't wonder why
|
||||
the compiler is telling you about unexpected whitespace. This should
|
||||
make your life easier.
|
||||
|
||||
* You can now read files from within the deploy. This can be used for
|
||||
templates or anything else. This was one of the last missing things
|
||||
that was blocking me from writing useful mcl modules.
|
||||
|
||||
* Fixed a copy-pasta bug where the != operator (for strings only) was
|
||||
actually doing an ==. Woops! The good news is that we've been shaking
|
||||
out silly bugs because I've been using mgmt more and more. Hopefully
|
||||
there aren't any woops ones like this left!
|
||||
|
||||
* A bunch of function, class, and include tests were added. We're
|
||||
getting really well tested!
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, etc
|
||||
resources if anyone is interested, reach out to us. Particularly if
|
||||
there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
BUGS
|
||||
|
||||
* Function values as args don't work yet. This is blocking us from
|
||||
implementing functions like map/reduce/filter, because they'd want to
|
||||
receive a function as input. TBH, I'm a bit tunnel-visioned on this,
|
||||
because I'm not the compilers genius that you are. If you can help,
|
||||
please let me know. I'll be posting a bunch of test cases that show
|
||||
what's needed shortly.
|
||||
|
||||
* Three patches have been submitted to mkosi to support the image
|
||||
building I've been doing. They're not merged yet, so you'll have to
|
||||
apply them yourself if you want to make your own distro images. This
|
||||
isn't a major requirement anyone should have, but if they're not
|
||||
merged, we'll store them here and apply them as needed.
|
||||
|
||||
TALKS
|
||||
|
||||
I'll be in Belgium for FOSDEM and CfgMgmtCamp in 2020. If I'm lucky
|
||||
I'll have at least one mgmt presentation. I might also consider going
|
||||
to DevConf.cz if I get a talk accepted. Feel to ping me if you'd like
|
||||
to hack, get consulting, training, etc while I'm in Europe!
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Freenode IRC, or ping this list if
|
||||
you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is fun and friendly! You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
bjanssens, Donald Bakong, James Shubin
|
||||
We had 3 unique committers since 0.0.20, and have had 69 overall.
|
||||
run 'git log 0.0.20..0.0.21' to see what has changed since 0.0.20
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
160
docs/release-notes/0.0.22
Normal file
160
docs/release-notes/0.0.22
Normal file
@@ -0,0 +1,160 @@
|
||||
I've just released version 0.0.22 of mgmt!
|
||||
|
||||
> 579 files changed, 17984 insertions(+), 3136 deletions(-)
|
||||
|
||||
Compared to the last release, this is a monster. Previously:
|
||||
|
||||
> 178 files changed, 4351 insertions(+), 829 deletions(-)
|
||||
|
||||
I apologize for not doing a release earlier, but to be quite honest,
|
||||
I've been busy, the people who are playing with mgmt are doing their
|
||||
own builds anyways, and there are still some unimplemented, pre-
|
||||
production features missing.
|
||||
|
||||
Also, I started a new job. News on my blog and how (if at all) it
|
||||
affects mgmt is there.
|
||||
|
||||
This is still an incredibly important release, so let's cover some of
|
||||
the points!
|
||||
|
||||
* This is the last release before we switch to go.mod
|
||||
* IRC channel moved to #mgmtconfig on libera.chat after Freenode died
|
||||
* New resources including tftp, dhcp, and http (all as servers, wow!)
|
||||
* New string interpolation implementation with many tests
|
||||
* Resource fields can accept complex structs and other types now
|
||||
* Improved type unification solver and new invariants like generator
|
||||
* A new polymorphic function API interface
|
||||
|
||||
And much more...
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are NOT available here for this release:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.22
|
||||
|
||||
NEWS
|
||||
|
||||
* The file resource supports building files from other "fragments".
|
||||
This is magic and automatic and real-time. Docs and examples are in the
|
||||
repo.
|
||||
|
||||
* The file resource (and others) have a new trait and queryable API to
|
||||
make decisions based on what other resources are in the graph.
|
||||
|
||||
* The file resource has a "purge" option to remove unmanaged files from
|
||||
a managed directory.
|
||||
|
||||
* A lot of built-in functions are auto-generated from the stdlib. Most
|
||||
things you would want are now present, particularly if they're pure
|
||||
functions.
|
||||
|
||||
* There's a new consul KV resource.
|
||||
|
||||
* File resources support symbolic modes now!
|
||||
|
||||
* New tftp related resources are now present. They're great and I use
|
||||
them to provision things!
|
||||
|
||||
* A docker image resource was added.
|
||||
|
||||
* First-class constants now exist. So you can do
|
||||
$const.res.file.state.exists instead of typing "exists" which is prone
|
||||
to typos. This is more verbose, but it's safer if that's your priority.
|
||||
|
||||
* We found a bug with fuzzing! Cool, thanks Patrick!
|
||||
|
||||
* We have dhcp server related resources. This is pretty cool when
|
||||
combined with the tftp resource and you can provision a lot of stuff
|
||||
from your laptop and one binary now.
|
||||
|
||||
* We also have http server resources. Combined with the tftp and dhcp
|
||||
resources mgmt starts to look like a powerful tool to greenfield a new
|
||||
datacentre and then take over and manage it continuous. All from a
|
||||
single, type-safe, code base. Of course you can do other things with
|
||||
this, and I'm looking forward to seeing the ideas that I haven't
|
||||
thought of yet! PS: An http:ui has been partially implemented too. Ping
|
||||
if you want to know more.
|
||||
|
||||
* Resource fields couldn't previously accept anonymous structs as types
|
||||
because of how golang built its reflect library. Joe found an elegant
|
||||
workaround, thanks!
|
||||
|
||||
* The type unification solver was improved to support some new
|
||||
invariants. This makes a lot of new things possible, and was done to
|
||||
support new complex functions including the eventual addition of map,
|
||||
reduce, and filter. One of the new invariants is a "generator"
|
||||
invariant, so that unification can take into account the entire
|
||||
relevant parts of the AST. It's not a textbook CS implementation, but
|
||||
it's based on sound theory I think, and it seems to work great. If you
|
||||
find an edge case, please let us know.
|
||||
|
||||
* The polymorphic function interface was changed to use the new
|
||||
unification logic. This makes a lot more sense. All the functions have
|
||||
been ported to the new interface.
|
||||
|
||||
* 42
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, etc
|
||||
resources if anyone is interested, reach out to us. Particularly if
|
||||
there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
BUGS
|
||||
|
||||
* Function values as args don't work yet. This is blocking us from
|
||||
implementing functions like map/reduce/filter, because they'd want to
|
||||
receive a function as input. TBH, I'm a bit tunnel-visioned on this,
|
||||
because I'm not the compilers genius that you are. If you can help,
|
||||
please let me know. I'll be posting a bunch of test cases that show
|
||||
what's needed shortly.
|
||||
|
||||
(This is the exact message I wrote last time. I've made a lot of
|
||||
progress since then, but motivation here has been low. Reach out if you
|
||||
can help.)
|
||||
|
||||
TALKS
|
||||
|
||||
Hopefully CfgMgmtCamp in 2022 will be back on. If we're lucky and safe,
|
||||
maybe I can travel there. TBD... Feel to ping me if you'd like to hack,
|
||||
talk, whatever if I'm in Europe.
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Libera IRC, or ping this list if
|
||||
you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
Although asking in IRC is the best way to find something to work on.
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is fun and friendly! You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
Adam Sigal, Ahmed Al-Hulaibi, David Randall, Derek Buckley, Donald
|
||||
Bakong, Felix Frank, Francois Rompre-Lanctot, Ivan Pejić, James Shubin,
|
||||
Jean-Philippe Evrard, Jimmy Tang, Joe Groocock, Jonathan Gold, Julien
|
||||
Pivotto, Kenneth Hoste, Matthew Lesko-Krleza, Patrick Meyer, viq, Yohan
|
||||
Belval
|
||||
We had 19 unique committers since 0.0.21, and have had 82 overall.
|
||||
run 'git log 0.0.21..0.0.22' to see what has changed since 0.0.21
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
102
docs/release-notes/0.0.23
Normal file
102
docs/release-notes/0.0.23
Normal file
@@ -0,0 +1,102 @@
|
||||
I've just released version 0.0.23 of mgmt!
|
||||
|
||||
> 424 files changed, 7051 insertions(+), 2256 deletions(-)
|
||||
|
||||
This is a fairly quiet release, and I'm mostly doing this to have a
|
||||
permanent tag before I start really breaking git master.
|
||||
|
||||
I'd like to apologize for things being kind of quiet. I've had to focus
|
||||
on life and making money to pay my bills, and I've been struggling a
|
||||
bit to complete some of the tougher algorithmic parts that I think are
|
||||
necessary for a solid MVP. Hopefully I will succeed, but to do so, it's
|
||||
going to be easier if I break git master and then sort things out
|
||||
later.
|
||||
|
||||
I'm feeling optimistic about the future, although help with some
|
||||
complex concurrent programming would certainly be appreciated.
|
||||
|
||||
With that, here are a few highlights from the release:
|
||||
|
||||
* We're using go.mod, but I'm not keeping it up-to-date regularly yet.
|
||||
|
||||
* We have an os.system(`cmd`) function!
|
||||
|
||||
* We replaced go-bindata with the embed package.
|
||||
|
||||
* We added a hetzner vm resource.
|
||||
|
||||
* We did a giant lang/ package refactor.
|
||||
|
||||
* Printf supports %v now (any type).
|
||||
|
||||
And much more...
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are NOT available here for this release:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.23
|
||||
|
||||
NEWS
|
||||
|
||||
* We have our Libera IRC channel bridged to Matrix, so you can join
|
||||
that way too.
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, Hetzner,
|
||||
etc, resources if anyone is interested, reach out to us. Particularly
|
||||
if there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
BUGS
|
||||
|
||||
* Function values as args don't work yet. This is blocking us from
|
||||
implementing functions like map/reduce/filter, because they'd want to
|
||||
receive a function as input.
|
||||
|
||||
(This is the exact message I wrote last time. I've made a lot of
|
||||
progress since then, but motivation here has been low. Reach out if you
|
||||
can help.)
|
||||
|
||||
TALKS
|
||||
|
||||
I don't have anything planned until CfgMgmtCamp 2024. If you'd like to
|
||||
book me for a private event, or sponsor my travel for your conference,
|
||||
please let me know.
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Libera IRC or Matrix, or ping this
|
||||
list if you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
Although asking in IRC is the best way to find something to work on.
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is fun and friendly! You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
dantefromhell, James Shubin, Jef Masereel, Joe Groocock, Samuel
|
||||
Gélineau
|
||||
We had 5 unique committers since 0.0.22, and have had 85 overall.
|
||||
run 'git log 0.0.22..0.0.23' to see what has changed since 0.0.22
|
||||
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
161
docs/release-notes/0.0.24
Normal file
161
docs/release-notes/0.0.24
Normal file
@@ -0,0 +1,161 @@
|
||||
I've just released version 0.0.24 of mgmt!
|
||||
|
||||
|
||||
> 600 files changed, 13622 insertions(+), 6907 deletions(-)
|
||||
|
||||
This is a huge and hugely important release! It has been a long time
|
||||
coming. We have lambdas! I could not have done this without the
|
||||
unrelentingly supportive and brilliant Samuel Gélineau.
|
||||
|
||||
Getting past the blockers and tricky code changes was all thanks to
|
||||
him. Cleanups, polish and making it more golang idiomatic was my doing.
|
||||
(The easy stuff.) If Sam wanted to be a golang expert, he could have
|
||||
done it all, but it was more sensible than I do all the mundane and
|
||||
filler stuff.
|
||||
|
||||
With that, here are a few highlights from the release:
|
||||
|
||||
* We have working lambdas, including iter.map =D
|
||||
|
||||
* We have a new function engine!
|
||||
|
||||
* We have improved type unification!
|
||||
|
||||
* We have an improved resource engine!
|
||||
|
||||
* We have improved many of the internal API's.
|
||||
|
||||
* We have so many tests.
|
||||
|
||||
And much more...
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are NOT available here for this release:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.24
|
||||
|
||||
NEWS
|
||||
|
||||
* Our main tests are now in the excellent .txtar format. This makes
|
||||
things much easier to manage.
|
||||
|
||||
* The iter.map function can be named as such due to parser tricks! No
|
||||
need to name it xmap anymore!
|
||||
|
||||
* The unification solver has been greatly improved. We can infer a lot
|
||||
more about function types.
|
||||
|
||||
* The resources API uses the context package for closing Watch and
|
||||
returning early from CheckApply. The next step would be to remove all
|
||||
the resource-specific timeout code and make that a metaparam.
|
||||
|
||||
* A new sync primitive has been added in case you'd like to use it
|
||||
somewhere. We'd love help adding an even more complex one. Look in
|
||||
util/sync.go for more information.
|
||||
|
||||
* Sam added some beautiful type inference debugging that makes things
|
||||
easier for those familiar with the standard literature.
|
||||
|
||||
* An important bug in standalone etcd has been fixed. While embedded
|
||||
etcd and automatic clustering isn't "supported" (it's buggy) the
|
||||
status-quo of using your own etcd cluster is stable, and you can even
|
||||
use the embedded etcd server in standalone mode...
|
||||
|
||||
* This means you can run `mgmt etcd` and get the standard etcd binary
|
||||
behaviour that you'd get from running `etcd` normally. This makes it
|
||||
easy to use both together since you only need to transport one binary
|
||||
around. (And maybe mgmt will do that for you!)
|
||||
|
||||
* I fixed and cleaned up some sketchy code in the resource engine. I
|
||||
had been unmotivated to fix this for a while because I really wanted
|
||||
lambdas first, but now that they are in, I took a good look at the
|
||||
code, made some fixes, and I'm really happy with it now.
|
||||
|
||||
* Metaparams are appropriately stateful between graph switches now.
|
||||
Retry is the easy example. Limit/Burst need to be ported if you care
|
||||
about these fine details.
|
||||
|
||||
* A RetryReset metaparam has been added. This is another good example
|
||||
of how powerful metaparams are and how much potential there is for
|
||||
building systems with these and future ones.
|
||||
|
||||
* A bunch of internal API's have been updated. This makes it better for
|
||||
function and resource writers! Some GAPI changes also got pushed
|
||||
through that make things clearer for those reading code.
|
||||
|
||||
* We have a listlookup function. It's still missing syntactic sugar
|
||||
though!
|
||||
|
||||
* Our new function graph engine is called "dage". I think it's pretty
|
||||
clever. There's a chance there is still a bug inside, but it's unclear.
|
||||
Please report any issues. If you have some large machines I can test
|
||||
very large and fast graphs on, please let me know.
|
||||
|
||||
* Lambdas really work now! The txn and ref/gc code is pretty fantastic.
|
||||
|
||||
* Many bugs have been killed!
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, Hetzner,
|
||||
etc, resources if anyone is interested, reach out to us. Particularly
|
||||
if there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
BUGS/TODO
|
||||
|
||||
* Function values getting _passed_ to resources doesn't work yet, but
|
||||
it's not a blocker, but it would definitely be useful. We're looking
|
||||
into it.
|
||||
|
||||
* Function graphs are unnecessarily dynamic. We might make them more
|
||||
static so that we don't need as many transactions. This is really a
|
||||
compiler optimization and not a bug, but it's something important we'd
|
||||
like to have.
|
||||
|
||||
* Running two Txn's during the same pause would be really helpful. I'm
|
||||
not sure how much of a performance improvement we'd get from this, but
|
||||
it would sure be interesting to build. If you want to build a fancy
|
||||
synchronization primitive, then let us know! Again this is not a bug.
|
||||
|
||||
TALKS
|
||||
|
||||
I don't have anything planned until CfgMgmtCamp 2024. If you'd like to
|
||||
book me for a private event, or sponsor my travel for your conference,
|
||||
please let me know.
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Libera IRC or Matrix, or ping this
|
||||
list if you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
Although asking in IRC/matrix is the best way to find something to work
|
||||
on.
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is fun and friendly! You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
James Shubin, Kaushal, Laurent Indermuehle, Ofek Atar, Samuel Gélineau
|
||||
We had 5 unique committers since 0.0.23, and have had 88 overall.
|
||||
run 'git log 0.0.23..0.0.24' to see what has changed since 0.0.23
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
343
docs/release-notes/0.0.25
Normal file
343
docs/release-notes/0.0.25
Normal file
@@ -0,0 +1,343 @@
|
||||
I've just released version 0.0.25 of mgmt!
|
||||
|
||||
> 686 files changed, 28391 insertions(+), 6935 deletions(-)
|
||||
|
||||
This is the first release that I consider to be generally useful at
|
||||
solving real-world problems, without needing to be an mgmt expert. It's
|
||||
also the first release that includes a very real `mcl` codebase. An
|
||||
accompanying blog post is also available:
|
||||
https://purpleidea.com/blog/2024/03/27/a-new-provisioning-tool/
|
||||
|
||||
With that, here are a few highlights from the release:
|
||||
|
||||
* We have a new mgmt partner program. Please sign-up for early access
|
||||
to these release notes, along with other special privileges. Details
|
||||
at: https://bit.ly/mgmt-partner-program
|
||||
|
||||
* You can build self-contained mgmt binaries that contain a custom
|
||||
application. An initial "provisioning tool" has been built in this way.
|
||||
Please see the blog post for more details.
|
||||
|
||||
* Default lookup functions exist in the language, with syntactic sugar
|
||||
(the || operator) , so you can get a default value if one doesn't
|
||||
exist, eg: $some_struct->some_struct_field || "some_default_value".
|
||||
|
||||
* Resource fields can now accept interface{} (any) types.
|
||||
|
||||
* A panic feature now exists in the language.
|
||||
|
||||
* The exec resource has new `donecmd` and `creates` fields. Of note,
|
||||
`creates` supports watches too!
|
||||
|
||||
* Send/recv now works for autogrouped resources!
|
||||
|
||||
* Added `include as` (for classes) to the language. Nested
|
||||
(sugar/hierarchical) classes are now supported to make this more
|
||||
powerful!
|
||||
|
||||
* Stats are printed if the function engine is waiting for too long.
|
||||
|
||||
* There's a new http:flag resource, and also an http:proxy resource so
|
||||
that we can have caching http proxies!
|
||||
|
||||
* Added a firewalld resource for opening ports!
|
||||
|
||||
* Added a dhcp:range resource which is very powerful and has a fun API!
|
||||
|
||||
* Added the "embedded" and "entry" packages, for building standalone
|
||||
tools. This goes perfectly with the new CLI library that we ported
|
||||
everything to.
|
||||
|
||||
And much more...
|
||||
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are available here for this release:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.25
|
||||
|
||||
They can also be found on the Fedora mirror:
|
||||
https://dl.fedoraproject.org/pub/alt/purpleidea/mgmt/releases/0.0.25/
|
||||
|
||||
|
||||
NEWS
|
||||
|
||||
* We changed the logical operators in mcl to use well-known English
|
||||
tokens: OR, AND, NOT. (but in lowercase of course)
|
||||
|
||||
* The history function has been temporarily removed from the syntactic
|
||||
core. We'll add it back if we find it's useful to have sugar!
|
||||
|
||||
* A bunch of lexer/parser cleanups and improvements were made.
|
||||
|
||||
* Default lookup functions for lists, maps, and structs have been
|
||||
added. These come with syntactic sugar as mentioned above. (We plan to
|
||||
keep this syntax, but we're open to feedback and changes if they're
|
||||
good.)
|
||||
|
||||
* Resources can accept the interface{} (any) type, although this should
|
||||
be used sparingly.
|
||||
|
||||
* We added a new mcl test suite that checks resource output too!
|
||||
|
||||
* Added a new `value` resource. This is a special resource kind that
|
||||
can be used for building some powerful state machines. Recommended for
|
||||
experienced users only.
|
||||
|
||||
* Improved the golang function generation to allow functions that take
|
||||
[]str, so now we have a bunch more functions (like join) in our stdlib
|
||||
for free.
|
||||
|
||||
* Add some mac address formatting functions. (core/net)
|
||||
|
||||
* Added a panic resource and panic function into the core language.
|
||||
This is useful for safely shutting down a running mcl program to
|
||||
prevent something disastrous or unhandled.
|
||||
|
||||
* Added a `donecmd` field to the exec resource. This runs a command
|
||||
after a successful CheckApply. This replaces the `&& echo foo > done`
|
||||
pattern that you'd see in some code.
|
||||
|
||||
* Added a new internal `local` API that can be used for local machine
|
||||
operations. So far, read/writing/watching values that are stored
|
||||
locally.
|
||||
|
||||
* Added `value` functions which bridge the `value` resource via the
|
||||
`local` API. To be used sparingly!
|
||||
|
||||
* Bumped to golang 1.20, and we'll probably move again before the next
|
||||
release.
|
||||
|
||||
* Allow send/recv with autogrouped resources. This adds many
|
||||
possibilities, in particular with the server style resources.
|
||||
|
||||
* Added a bunch of tests for sneaky corner cases. Some of these were
|
||||
hard to write, but I think they're worth it.
|
||||
|
||||
* ExprBind is now monomorphic! This was a design mistake that we
|
||||
introduced, but have since repaired. We now have far fewer copies
|
||||
running in the function graph, and things are much more efficient. This
|
||||
means lambdas can only have one type when used at two different call
|
||||
sites, which is much more logical, safer, faster and memory efficient.
|
||||
|
||||
* Added an --only-unify option if you want to test your code but not
|
||||
run it.
|
||||
|
||||
* Added a concat function for the common case of interpolation. This
|
||||
makes type unification significantly faster.
|
||||
|
||||
* Eliminated some "benign" races. You might find this commit
|
||||
interesting to read: bc63b7608e84f60bf9d568188814d411a0688738
|
||||
|
||||
* A pgraph bug was found and fixed. A test was added too! It's amazing
|
||||
this was here for so long, it just shows how subtle graph
|
||||
datastructures can be.
|
||||
|
||||
* Added `include as` (for classes) to the language which lets our
|
||||
classes produce values which can then be used elsewhere. I decided this
|
||||
feature would be necessary after writing a bunch of mcl. It does have
|
||||
an extraneous scoping bug, but not anything that causes problems.
|
||||
|
||||
* Nested classes are now supported. This lets you write the equivalent
|
||||
of nested classes, without actually having to nest them! This is not
|
||||
inheritance, but rather a way of handling scope and passing it
|
||||
downwards.
|
||||
|
||||
* Improved the Ordering compiler step to catch a bunch of unhandled
|
||||
bugs. Sam is a genius and was able to figure out some of these using
|
||||
wizardry.
|
||||
|
||||
* Added some convert functions to the mcl package.
|
||||
|
||||
* Allow edges with colons...
|
||||
|
||||
* ...Because we now support a new hierarchical autogrouping algorithm!
|
||||
This let's us have some very powerful resources.
|
||||
|
||||
* ...Like http:*, dhcp:*, and so on, but we could even go deeper!
|
||||
|
||||
* Fixed a super sneaky bug with resource swapping. Due to how we Cmp,
|
||||
this now preserves state more often, and in particular when we need it.
|
||||
I'm fairly certain that some code in a WIP branch of mine was actually
|
||||
blocked because of this issue. Pleased to run into it again, but now
|
||||
with a fix in place!
|
||||
|
||||
* Added an http:flag resource. This let's a `wget` or similar call back
|
||||
to the http:server to kick off an action.
|
||||
|
||||
* The http:flag resource supports directories now.
|
||||
|
||||
* Stats are printed if the function engine is waiting for too long.
|
||||
This is mostly useful for developers who are building new functions and
|
||||
have a bug in their midst!
|
||||
|
||||
* We added a --skip-unify option to prevent the double unification when
|
||||
running locally. When using `mgmt run` to test locally, we type check,
|
||||
and then deploy to ourselves, which then naturally type checks again.
|
||||
This skips the first one, which would be unsafe generally, but is
|
||||
perfectly safe when we're running a single instance.
|
||||
|
||||
* Added a new http:proxy resource, and then tweaked it's API, and then
|
||||
added http streaming. This is an incredibly powerful resource that lets
|
||||
us build a caching http proxy with a single resource. I can't wait to
|
||||
see what else it gets used for. I'm using it for provisioning. It's not
|
||||
performance optimized at the moment as it uses a single mutex for
|
||||
everything, but this could be extended if we wanted to scale this out.
|
||||
|
||||
* Added a ton of measuring/timing of common operations. This confirmed
|
||||
my belief that autoedges were slower than necessary. There are two ways
|
||||
to improve this. We might end up doing either one or both. Autogrouping
|
||||
is currently much faster than needed, so no improvements planned for
|
||||
now!
|
||||
|
||||
* Started to clean up the internal FS API's. It would be really great
|
||||
if the core golang team would add something so we could get rid of the
|
||||
afero external interfaces.
|
||||
|
||||
* Added an "embedded" package to offer API's related to embedded mcl
|
||||
programs! This lets us build standalone binaries which are powered by
|
||||
mcl.
|
||||
|
||||
* Moved to a new CLI (go-arg) library. This has a few downsides, but
|
||||
they are fixable upstream, and this vastly improved our code quality
|
||||
and API's. This needed to happen, what with the mess that was
|
||||
urfave/cli. Look at our diff's, they're really elegant! This let us
|
||||
clean up our lib structs as well!
|
||||
|
||||
* Added an "entry" package to kick-off the embedded API stuff. This
|
||||
uses the new CLI API's that we just built. The end-user now has a
|
||||
really easy time building new tools.
|
||||
|
||||
* Added a bunch of util functions to aid in building certain standalone
|
||||
tools. I'm willing to accept more contributions in this space if
|
||||
they're sane, and related to our general mission. Please ask and then
|
||||
send patches if you're unsure.
|
||||
|
||||
* Added a firewalld resource which makes opening up ports automatic
|
||||
when we need them. Perfect for the standalone laptop use-case.
|
||||
|
||||
* Made type unification cancellable in case you get into a long-running
|
||||
scenario and want to end early.
|
||||
|
||||
* Added a `creates` field to the exec resource. Very useful, and also
|
||||
supports watches! This is very useful for the common uses of exec.
|
||||
|
||||
* Added a dhcp:range resource to offer any number of IP addresses to
|
||||
devices that we don't know the mac addresses of in advance. This makes
|
||||
building a provisioning tool even more ergonomic.
|
||||
|
||||
* Optimized the name invariants since we can usually avoid an exclusive
|
||||
invariant in the common case. This roughly halved the type unification
|
||||
time. More improvements coming too!
|
||||
|
||||
* Caught a sneaky list type that could get through type unification
|
||||
when it was interpolated alone. This now enforces the string invariant
|
||||
when we specify it, which is an important language design distinction.
|
||||
We added tests for this of course too!
|
||||
|
||||
* The "log" package has been entirely refactored and is only visible in
|
||||
one place at the top of the program. Nice! I have a design for a
|
||||
"better logger / user interface" if we ever want to improve on this.
|
||||
|
||||
* Added release targets for standalone binary builds. I also improved
|
||||
the Makefile release magic significantly.
|
||||
|
||||
* Made a lot of small "polish" improvements to various resources.
|
||||
|
||||
* Most interestingly, an embedded provisioner application has been
|
||||
built and made available in full. Please test and share with others.
|
||||
Hopefully this will encourage more interest in the project.
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, Hetzner,
|
||||
etc, resources if anyone is interested, reach out to us. Particularly
|
||||
if there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
|
||||
BUGS/TODO
|
||||
|
||||
* Function values getting _passed_ to resources doesn't work yet, but
|
||||
it's not a blocker, but it would definitely be useful. We're looking
|
||||
into it.
|
||||
|
||||
* Function graphs are unnecessarily dynamic. We might make them more
|
||||
static so that we don't need as many transactions. This is really a
|
||||
compiler optimization and not a bug, but it's something important we'd
|
||||
like to have.
|
||||
|
||||
* Running two Txn's during the same pause would be really helpful. I'm
|
||||
not sure how much of a performance improvement we'd get from this, but
|
||||
it would sure be interesting to build. If you want to build a fancy
|
||||
synchronization primitive, then let us know! Again this is not a bug.
|
||||
|
||||
* Type unification performance can be improved drastically. I will have
|
||||
to implement the fast algorithm so that we can scale to very large mcl
|
||||
programs. Help is wanted if you are familiar with "unionfind" and/or
|
||||
type unification.
|
||||
|
||||
|
||||
TALKS
|
||||
|
||||
I don't have anything planned until CfgMgmtCamp 2025. If you'd like to
|
||||
book me for a private event, or sponsor my travel for your conference,
|
||||
please let me know.
|
||||
|
||||
I recently gave two talks: one at CfgMgmtCamp 2024, and one at FOSDEM
|
||||
in the golang room. Both are available online and demonstrated an
|
||||
earlier version of the provisioning tool which is fully available
|
||||
today. The talks can be found here: https://purpleidea.com/talks/
|
||||
|
||||
|
||||
PARTNER PROGRAM
|
||||
|
||||
We have a new mgmt partner program which gets you early access to
|
||||
releases, bug fixes, support, and many other goodies. Please sign-up
|
||||
today: https://bit.ly/mgmt-partner-program
|
||||
|
||||
|
||||
MISC
|
||||
|
||||
Our mailing list host (Red Hat) is no longer letting non-Red Hat
|
||||
employees use their infrastructure. We're looking for a new home. I've
|
||||
opened a ticket with Freedesktop. If you have any sway with them or
|
||||
other recommendations, please let me know:
|
||||
https://gitlab.freedesktop.org/freedesktop/freedesktop/-/issues/1082
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Libera IRC or Matrix (preferred) and
|
||||
ping us if you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
Although asking in IRC/matrix is the best way to find something to work
|
||||
on.
|
||||
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is fun and friendly! You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
Eng Zer Jun, James Shubin, Oliver Lowe, Samuel Gélineau
|
||||
We had 4 unique committers since 0.0.24, and have had 90 overall.
|
||||
run 'git log 0.0.24..0.0.25' to see what has changed since 0.0.24
|
||||
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
142
docs/release-notes/0.0.26
Normal file
142
docs/release-notes/0.0.26
Normal file
@@ -0,0 +1,142 @@
|
||||
I've just released version 0.0.26 of mgmt!
|
||||
|
||||
> 16 files changed, 869 insertions(+), 181 deletions(-)
|
||||
|
||||
Hot off the heels of the recent large release (0.0.25) I've just
|
||||
released an incremental update...
|
||||
|
||||
See more here:
|
||||
|
||||
https://purpleidea.com/blog/2024/03/27/a-new-provisioning-tool/
|
||||
|
||||
With that, here are a few highlights from the release:
|
||||
|
||||
* We have a new mgmt partner program. Please sign-up for early access
|
||||
to these release notes, along with other special privileges. Details
|
||||
at: https://bit.ly/mgmt-partner-program
|
||||
|
||||
* Type unification for the provisioning tool is about 40x faster.
|
||||
|
||||
* We fix a small bug related to the upcoming fedora 40 release.
|
||||
|
||||
And much more...
|
||||
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are available here for this release:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.26
|
||||
|
||||
They can also be found on the Fedora mirror:
|
||||
https://dl.fedoraproject.org/pub/alt/purpleidea/mgmt/releases/0.0.26/
|
||||
|
||||
|
||||
NEWS
|
||||
|
||||
* Added old release notes into git
|
||||
|
||||
* We now skip over unreleased Fedora versions (like "40 Beta") when
|
||||
trying to automatically determine the latest stable release.
|
||||
|
||||
* Type unification was structurally refactored to make way for a bunch
|
||||
of future improvements and generally to modernize the code.
|
||||
|
||||
* Added some unification optimizations and a unification flag
|
||||
optimizations system to allow solvers to support special flags. One of
|
||||
these new flags was used for the provisioner code with a substantial
|
||||
improvement in type unification time by about 40x.
|
||||
|
||||
* New cli args are also available for using these flags.
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, Hetzner,
|
||||
etc, resources if anyone is interested, reach out to us. Particularly
|
||||
if there is support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
|
||||
BUGS/TODO
|
||||
|
||||
* Function values getting _passed_ to resources doesn't work yet, but
|
||||
it's not a blocker, but it would definitely be useful. We're looking
|
||||
into it.
|
||||
|
||||
* Function graphs are unnecessarily dynamic. We might make them more
|
||||
static so that we don't need as many transactions. This is really a
|
||||
compiler optimization and not a bug, but it's something important we'd
|
||||
like to have.
|
||||
|
||||
* Running two Txn's during the same pause would be really helpful. I'm
|
||||
not sure how much of a performance improvement we'd get from this, but
|
||||
it would sure be interesting to build. If you want to build a fancy
|
||||
synchronization primitive, then let us know! Again this is not a bug.
|
||||
|
||||
* General type unification performance can be improved drastically. I
|
||||
will have to implement the fast algorithm so that we can scale to very
|
||||
large mcl programs. Help is wanted if you are familiar with "unionfind"
|
||||
and/or type unification.
|
||||
|
||||
|
||||
TALKS
|
||||
|
||||
I don't have anything planned until CfgMgmtCamp 2025. If you'd like to
|
||||
book me for a private event, or sponsor my travel for your conference,
|
||||
please let me know.
|
||||
|
||||
I recently gave two talks: one at CfgMgmtCamp 2024, and one at FOSDEM
|
||||
in the golang room. Both are available online and demonstrated an
|
||||
earlier version of the provisioning tool which is fully available
|
||||
today. The talks can be found here: https://purpleidea.com/talks/
|
||||
|
||||
|
||||
PARTNER PROGRAM
|
||||
|
||||
We have a new mgmt partner program which gets you early access to
|
||||
releases, bug fixes, support, and many other goodies. Please sign-up
|
||||
today: https://bit.ly/mgmt-partner-program
|
||||
|
||||
|
||||
MISC
|
||||
|
||||
Our mailing list host (Red Hat) is no longer letting non-Red Hat
|
||||
employees use their infrastructure. We're looking for a new home. I've
|
||||
opened a ticket with Freedesktop. If you have any sway with them or
|
||||
other recommendations, please let me know:
|
||||
https://gitlab.freedesktop.org/freedesktop/freedesktop/-/issues/1082
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium
|
||||
and hard issues available! You're also welcome to suggest your own!
|
||||
Please join us in #mgmtconfig on Libera IRC or Matrix (preferred) and
|
||||
ping us if you'd like help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-con
|
||||
tribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%
|
||||
3Amgmtlove
|
||||
|
||||
Although asking in IRC/matrix is the best way to find something to work
|
||||
on.
|
||||
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get
|
||||
involved. This is fun and friendly! You get to improve your skills,
|
||||
and we get some patches in return. Ping me off-list for details.
|
||||
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release:
|
||||
James Shubin
|
||||
We had 1 unique committers since 0.0.25, and have had 90 overall.
|
||||
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
205
docs/release-notes/0.0.27
Normal file
205
docs/release-notes/0.0.27
Normal file
@@ -0,0 +1,205 @@
|
||||
I've just released version 0.0.27 of mgmt!
|
||||
|
||||
> 854 files changed, 28882 insertions(+), 16049 deletions(-)
|
||||
|
||||
This is rather large release, as I'm not making regular releases unless there's
|
||||
a specific ask. Most folks that are playing with mgmt are using `git master`.
|
||||
|
||||
With that, here are a few highlights from the release:
|
||||
|
||||
* Type unification is now extremely fast for all scenarios.
|
||||
|
||||
* Added a modules/ directory with shared mcl code for everyone to use. This
|
||||
includes code for virtualization, cups, shorewall, dhcp, routers, and more!
|
||||
|
||||
* New core mgmt commands including setup, firstboot, and docs were added!
|
||||
|
||||
* The provisioner got lots of improvements including handoff, and iPXE support.
|
||||
|
||||
And much more...
|
||||
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are available here for this release:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/0.0.27
|
||||
|
||||
They can also be found on the Fedora mirror:
|
||||
https://dl.fedoraproject.org/pub/alt/purpleidea/mgmt/releases/0.0.27/
|
||||
|
||||
|
||||
NEWS
|
||||
|
||||
* Primary community channel is now on Matrix. IRC is deprecated until someone
|
||||
wants to run a bridge for us.
|
||||
|
||||
* Type unification is now textbook, and blazingly (linearly) fast. The large
|
||||
programs I'm writing now unify in under 200ms. Most small programs typically
|
||||
unify in ~5ms.
|
||||
|
||||
* Resource and edge names are always lists of strings now unless they're static.
|
||||
|
||||
* We're up to golang 1.23 now. Older versions may still work.
|
||||
|
||||
* Our type system now supports unification variables like ?1, ?2 and so on.
|
||||
|
||||
* I fixed a bug in my contrib.sh script which omitted the Co-authored-by people!
|
||||
This means Samuel Gélineau might have previously been missed in past release
|
||||
notes which is tragic, since he has been by far the most important contributor
|
||||
to mgmt.
|
||||
|
||||
* Made toposort deterministic which fixes some spurious non-determinism.
|
||||
|
||||
* Added the iterator filter function. (An important core primitive.)
|
||||
|
||||
* Cleaned up the output of many resources to make logs more useful / less noisy.
|
||||
|
||||
* Added constants, although I plan to change this to a `const` import package.
|
||||
|
||||
* Added the list and map core packages.
|
||||
|
||||
* Catch $ in metaparams to make the obvious bug cases easier for users to avoid.
|
||||
|
||||
* Consul is now behind a build tag for now, since it's non-free. We'll remove it
|
||||
eventually if there isn't a suitable free replacement.
|
||||
|
||||
* Added mcl modules directory with a good initial set of interesting code.
|
||||
|
||||
* Added the the "vardir" API to our "local" package. This is a helpful primitive
|
||||
which I use in almost every module that I write.
|
||||
|
||||
* Added a gzip resource!
|
||||
|
||||
* Added a tar resource!
|
||||
|
||||
* We moved the template() function to the golang.template namespace. This makes
|
||||
it clear what kind of template it is and de-emphasizes our "love" for it as the
|
||||
blessed template engine at least for now.
|
||||
|
||||
* Added a sysctl resource!
|
||||
|
||||
* Added a virt-builder resource for building images. We can now automate virtual
|
||||
machines really elegantly.
|
||||
|
||||
* A bunch of core functions were added including stuff in net, strings, deploy,
|
||||
and more!
|
||||
|
||||
* The local package got a neat "pool" function. There are lots of possibilities
|
||||
to use this in creative ways!
|
||||
|
||||
* The GAPI/deploy code got more testing and we found some edge cases and patched
|
||||
them. You can now deploy in all sorts of creative ways and things should work
|
||||
as expected!
|
||||
|
||||
* Added a resource for archiving a deploy. This is deploy:tar and helps with
|
||||
bootstrapping new machines.
|
||||
|
||||
* Found a sneaky DHCP bug and fixed it!
|
||||
|
||||
* Added mgmt setup and firstboot commands! This helps bootstrap things without
|
||||
needing to re-implement that logic everywhere as bash too!
|
||||
|
||||
* Added a "docs" command for generating resources and function documentation!
|
||||
|
||||
* The provisioner got lots of improvements including handoff, and iPXE support.
|
||||
|
||||
* New mcl modules include shorewall, dhcp, cups, some meta modules, misc modules
|
||||
and more!
|
||||
|
||||
* Added a BMC resource in case you want to automate your server hardware.
|
||||
|
||||
* We now allow multiple star (*) imports although it's not recommended.
|
||||
|
||||
* Hostname handoff is now also part of the provisioner.
|
||||
|
||||
* Fixed two type unification corner cases with magic struct functions.
|
||||
|
||||
* Added iPXE support to the provisioner.
|
||||
|
||||
* Added pprof support to make it easy to generate performance information.
|
||||
|
||||
* Added anonymous function calling. These are occasionally useful, and now the
|
||||
language has them. They were fun and concise to implement!
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, Hetzner, etc,
|
||||
resources if anyone is interested, reach out to us. Particularly if there is
|
||||
support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
|
||||
BUGS/TODO
|
||||
|
||||
* Function values getting _passed_ to resources doesn't work yet, but it's not a
|
||||
blocker, but it would definitely be useful. We're looking into it.
|
||||
|
||||
* Function graphs are unnecessarily dynamic. We might make them more static so
|
||||
that we don't need as many transactions. This is really a compiler optimization
|
||||
and not a bug, but it's something important we'd like to have.
|
||||
|
||||
* Running two Txn's during the same pause would be really helpful. I'm not sure
|
||||
how much of a performance improvement we'd get from this, but it would sure be
|
||||
interesting to build. If you want to build a fancy synchronization primitive,
|
||||
then let us know! Again this is not a bug.
|
||||
|
||||
* The arm64 version doesn't support augeas, so it was built with:
|
||||
GOTAGS='noaugeas' to get the build out.
|
||||
|
||||
|
||||
TALKS
|
||||
|
||||
After FOSDEM/CfgMgmtCamp 2025, I don't have anything planned until CfgMgmtCamp
|
||||
2026. If you'd like to book me for a private event, or sponsor my travel for
|
||||
your conference, please let me know.
|
||||
|
||||
|
||||
PARTNER PROGRAM
|
||||
|
||||
Interest in the partner program has been limited to small individuals with no
|
||||
real corporate backing, so its been officially discontinued for now. If you're
|
||||
interested in partnering with us and receiving support, mgmt products early
|
||||
access to releases, bug fixes, support, and many other goodies, please sign-up
|
||||
today: https://bit.ly/mgmt-partner-program
|
||||
|
||||
|
||||
MISC
|
||||
|
||||
Our mailing list host (Red Hat) is no longer letting non-Red Hat employees use
|
||||
their infrastructure. We're looking for a new home. I've opened a ticket with
|
||||
Freedesktop. If you have any sway with them or other recommendations, please let
|
||||
me know:
|
||||
https://gitlab.freedesktop.org/freedesktop/freedesktop/-/issues/1082
|
||||
|
||||
We're still looking for new contributors, and while there are easy, medium and
|
||||
hard issues available! You're also welcome to suggest your own! Please join us
|
||||
in #mgmtconfig on Libera IRC or Matrix (preferred) and ping us if you'd like
|
||||
help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-contribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%3Amgmtlove
|
||||
|
||||
Although asking in matrix is the best way to find something to work on.
|
||||
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get involved. This is
|
||||
fun and friendly! You get to improve your skills, and we get some patches in
|
||||
return. Ping me off-list for details.
|
||||
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest release:
|
||||
Cian Yong Leow, Felix Frank, James Shubin, Joe Groocock, Julian Rüth, Omar Al-Shuha, Samuel Gélineau, xlai89
|
||||
We had 8 unique committers since 0.0.26, and have had 96 overall.
|
||||
Run 'git log 0.0.26..0.0.27' to see what has changed since 0.0.26
|
||||
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
71
docs/release-notes/0.0.9
Normal file
71
docs/release-notes/0.0.9
Normal file
@@ -0,0 +1,71 @@
|
||||
I've just released 0.0.9!
|
||||
|
||||
Release tags are now signed with my GPG key.
|
||||
|
||||
From now on, I'm going to try and write a release email to the list
|
||||
with information about each release. Here we go!
|
||||
|
||||
NEWS
|
||||
|
||||
* There were far more commits that went into this release than I was
|
||||
expecting. I delayed a while because of some nasty races and deadlocks
|
||||
I encountered when cleaning some cruft out of the engine.
|
||||
|
||||
134 files changed, 5394 insertions(+), 2168 deletions(-)
|
||||
|
||||
* We grew initial integration with Prometheus thanks to new contributor
|
||||
`roidelapluie`.
|
||||
|
||||
* The file resource can now chown/chmod files! Thanks to new
|
||||
contributor `mildred`.
|
||||
|
||||
* The virt resource can now hotplug/hotunplug cpus. This made for some
|
||||
dope demos at CfgMgmtCamp. Special thanks to pkrempa for helping me
|
||||
with the libvirt API.
|
||||
|
||||
* Native testing of all the golang code is now enabled again. I broke
|
||||
this when I split mgmt up into multiple packages. (Sorry!) Golang tests
|
||||
in foo_test.go should now run and be tested automatically. I'd
|
||||
especially like it if someone wrote some for pgraph/pgraph.go:
|
||||
GraphSync().
|
||||
|
||||
* Documentation has been significantly improved. We have a resource
|
||||
guide (for writing new native resources) and much more. We also now
|
||||
build the docs as a RTD project:
|
||||
https://mgmt.readthedocs.io/en/master/
|
||||
Patches welcome to make it style as pretty as GitHub markdown does.
|
||||
|
||||
* See the git log for more NEWS, and sorry for anything notable I left
|
||||
out!
|
||||
|
||||
BUGS
|
||||
|
||||
* I hope to resolve this soon, but it shouldn't block further
|
||||
development on git master for now. The issue: if you make extremely
|
||||
high speed graph changes to graphs involving the virt resource, it can
|
||||
eventually cause a panic. This is being tracked here:
|
||||
https://github.com/libvirt/libvirt-go/issues/7 (help welcome!)
|
||||
|
||||
GLUSTERFS
|
||||
|
||||
I've started working on some stuff for Glusterd so that it can embed
|
||||
mgmt as a lib, and use the resource model to simplify cluster
|
||||
management. This will involve writing Gluster resources in mgmt
|
||||
(volume, brick, etc...) and all sorts of other fun stuff. If you'd like
|
||||
to participate, please LMK!
|
||||
|
||||
MISC
|
||||
|
||||
We're still looking for new contributors, and there are easy, medium and hard issues available! You're also welcome to suggest your own! Please join us in #mgmtconfig on Freenode IRC, or ping this list if you'd like help getting started!
|
||||
|
||||
THANKS
|
||||
|
||||
We had 12 unique committers since 0.0.8, and have had 29 overall.
|
||||
Thanks (alphabetically) to everyone who contributed to the latest
|
||||
release: Daniele Sluijters, Daniel P. Berrange, Felix Frank, goberghen,
|
||||
James Shubin, Julien Pivotto, Kaushal M, Lars Kulseng, Mildred Ki'Lya,
|
||||
Sean Jones, Steve Milner, Tom Ritserveldt.
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
280
docs/release-notes/1.0.0
Normal file
280
docs/release-notes/1.0.0
Normal file
@@ -0,0 +1,280 @@
|
||||
I've just released version 1.0.0 of mgmt!
|
||||
|
||||
> 614 files changed, 30199 insertions(+), 11916 deletions(-)
|
||||
|
||||
This is very important and large release. It's been 10 years since I first
|
||||
publicly released this project, and I might as well stop confusing new users.
|
||||
I'm happily using it in production for some time now, and I love writing `mcl`
|
||||
every day! I am doing customer work in mgmt, and I have happy users.
|
||||
|
||||
With that, here are a few highlights from the release:
|
||||
|
||||
* There is a new function engine which is significantly faster on large graphs.
|
||||
It could be improved further, but the optimizations aren't needed for now.
|
||||
|
||||
* The "automatic embedded etcd clustering" should be considered deprecated. You
|
||||
can run with --no-magic to ensure it's off. It was buggy and we will possibly
|
||||
write it with mcl anyways. Expect it to be removed soon.
|
||||
|
||||
* Type unification errors have context and line numbers! Many other error
|
||||
scenarios have this too! This isn't perfect, and there are still some remaining
|
||||
places when you don't get this information. Please help us find and expand
|
||||
these.
|
||||
|
||||
* The function API has been overhauled which now makes writing most functions
|
||||
significantly easier and simpler. They'll also use less memory. This is a
|
||||
benefit of the new function engine.
|
||||
|
||||
* We have added *declarative* for and forkv statements to the language.
|
||||
|
||||
* Exported resources are merged and gorgeous! They work how I've always wanted.
|
||||
You can actually see my experiment in the very first demo of mgmt, and I finally
|
||||
wrote them to work with the language how I've always wanted.
|
||||
|
||||
* There's an http:server:ui set of resources that have been added. Check out:
|
||||
https://www.youtube.com/watch?v=8vz1MMGkuik for some examples of that in action
|
||||
and more!
|
||||
|
||||
And much more...
|
||||
|
||||
|
||||
SPONSORS
|
||||
|
||||
The `mgmt` project is generously sponsored by:
|
||||
|
||||
m9rx corporation - https://m9rx.com/
|
||||
|
||||
Please reach out if you'd like to sponsor!
|
||||
|
||||
|
||||
DOWNLOAD
|
||||
|
||||
Prebuilt binaries are available here for this release:
|
||||
https://github.com/purpleidea/mgmt/releases/tag/1.0.0
|
||||
|
||||
They can also be found on the Fedora mirror:
|
||||
https://dl.fedoraproject.org/pub/alt/purpleidea/mgmt/releases/1.0.0/
|
||||
|
||||
|
||||
NEWS
|
||||
|
||||
* A bunch of misc mcl code has been added to modules/ for you to see.
|
||||
|
||||
* The user resource has been improved following feedback from cloudflare.
|
||||
|
||||
* Detect self-referential frags when building files that way.
|
||||
|
||||
* Added a new function for URL parsing.
|
||||
|
||||
* Type unification errors have context and line numbers!
|
||||
|
||||
* There's a "baddev" feature branch which gets rebased which you can use if you
|
||||
don't want to install the tools to compiler the lexer/parser stuff. We do the
|
||||
ugly commit for you if that's easier for development.
|
||||
|
||||
* We have added *declarative* for and forkv statements to the language. If you
|
||||
know of a better name that "forkv" we're happy to hear it, but a small poll
|
||||
didn't produce a more convincing suggestion.
|
||||
|
||||
* Waiting for a deploy just happens automatically with the "empty" frontend.
|
||||
|
||||
* Waiting to run a deploy just waits automatically until etcd is online.
|
||||
|
||||
* Automatic mgmt deploying after virt provisioning works with a seeds field.
|
||||
|
||||
* There's a global flag to skip autoedges to improve performance.
|
||||
|
||||
* The docker resource has been modernized and supports running on a docker host
|
||||
that we're bootstrapping.
|
||||
|
||||
* Docker ports were built backwards and these have been corrected.
|
||||
|
||||
* The "world" interface has been cleaned up dramatically. This will make life
|
||||
easier for someone who wants to add a new backend there. Filesystem, scheduler,
|
||||
deployer, and more are all split.
|
||||
|
||||
* We can run our etcd connection over SSH. That's one of the new backends.
|
||||
There's actually a reconnect issue, but it's an easy fix and it should likely
|
||||
come out in the next release.
|
||||
|
||||
* We have an is_virtual function to detect where mgmt is running!
|
||||
|
||||
* Virtualization modules moved to qcow2 by default. It's solid.
|
||||
|
||||
* Improved a lot of user-facing logging so it's clearer what's happening.
|
||||
|
||||
* Exported resources have been implemented ... and they're glorious. They work
|
||||
how I've always dreamed, and are such a breath of fresh air from the Puppet
|
||||
days. There's an export/collect system. Export works by metaparam, not a special
|
||||
language feature, and collect works with core functions. It runs when the
|
||||
resource in the graph actually runs, as opposed to "all at once, even if you
|
||||
fail" like the old days. Yay!
|
||||
|
||||
* fmt.printf handles more cases!
|
||||
|
||||
* The file resource now has a symlink param. Someone test it and find issues.
|
||||
|
||||
* We have an iter.range function which is helpful with `for` statements.
|
||||
|
||||
* We do some speculation which drastically reduces the shape of the function
|
||||
graphs in a lot of constant scenarios. This also reduces the need to change the
|
||||
shape, which brings a huge performance boost.
|
||||
|
||||
* Don't reuse fact pointers. There was a bug around those. In fact get rid of
|
||||
the fact API since it's pointless really.
|
||||
|
||||
* There's some new stuff in the convert package.
|
||||
|
||||
* We added an http:server:ui resource. This is kind of a prototype, but you can
|
||||
see it in action here: https://www.youtube.com/watch?v=8vz1MMGkuik
|
||||
|
||||
* Fix some send/recv corner cases. I wish I had more tests for this. Hint!
|
||||
|
||||
* There's an os.readfilewait() function in temporarily. This will go away when
|
||||
we get the <|> operator.
|
||||
|
||||
* A WatchFiles field was added to the exec resource. Very handy.
|
||||
|
||||
* We have a new "line" resource. It supports "trim"-ing too.
|
||||
|
||||
* There are some new functions that have been added.
|
||||
|
||||
* The modules/ directory got some ssh key things.
|
||||
|
||||
* Automatic grouping logic improved, thanks to http:server:ui stuff.
|
||||
|
||||
* Hierarchical grouping works very reliably as far as I can tell.
|
||||
|
||||
* A bunch of ctx's were added all over where they never were. Legacy code!
|
||||
|
||||
* A bunch of network/NetworkManager/networkd and related mcl code was added. The
|
||||
interfaces are really ugly, what is the correct upstream network config thing?
|
||||
|
||||
* We have a modinfo function.
|
||||
|
||||
* We built in some ulimit settings for modern machines.
|
||||
|
||||
* We have an mcl class for copr setup.
|
||||
|
||||
* We added SSH hostkey logic into our core etcd ssh connection tooling.
|
||||
|
||||
* The provisioner supports exec handoff. It can also handle more scenarios, eg
|
||||
booting from an ipxe usb key and not installing on it.
|
||||
|
||||
* The provisioner supports encrypting machines with LUKS. It does this in a very
|
||||
clever way to allow creation of secure passwords after first boot. Many kudos to
|
||||
the systemd and other authors who built all the needed pieces for this to just
|
||||
work fairly well.
|
||||
|
||||
* We improved a graph function from O(n) to O(1). Woops =D
|
||||
|
||||
* We removed the secret channels from the function graphs. This is much simpler
|
||||
now!
|
||||
|
||||
* ExprIf and StmtIf both do the more correct thing. I guess the bigger graph was
|
||||
eventually going to need to get killed. This was a good choice that I didn't
|
||||
make soon enough.
|
||||
|
||||
* A ton of races were killed. We're building by default with the race checker.
|
||||
I don't know why I didn't do this ten years ago. Performance is not so terrible
|
||||
these days, and it catches so much. Woops. Good lesson to share with others.
|
||||
|
||||
* The language has a nil type, but don't worry, this is only for internal
|
||||
plumbing, and we will NOT let it be user facing!
|
||||
|
||||
* The langpuppet stuff had to be removed again for now. If it's used, patch in.
|
||||
|
||||
* The GAPI stuff got a major cleanup. It was early code that was bad. Now it's a
|
||||
lot better.
|
||||
|
||||
* The new function engine is the really big story. Have a look if you're an
|
||||
algorithmist. We'd love to have people work on improving it further. It's most
|
||||
likely glitch free now too!
|
||||
|
||||
* The virt resource code a big cleanup. It runs hotplug again which had rotted
|
||||
due to libvirt api changes I think.
|
||||
|
||||
* The qemu guest agent works automatically again.
|
||||
|
||||
* The svc resource (one of the earliest) has been overhauled since it had old
|
||||
buggy code which has now been fixed.
|
||||
|
||||
* We're looking for help writing Amazon, Google, DigitalOcean, Hetzner, etc,
|
||||
resources if anyone is interested, reach out to us. Particularly if there is
|
||||
support from those organizations as well.
|
||||
|
||||
* Many other bug fixes, changes, etc...
|
||||
|
||||
* See the git log for more NEWS, and for anything notable I left out!
|
||||
|
||||
|
||||
BUGS/TODO
|
||||
|
||||
* Function values getting _passed_ to resources doesn't work yet, but it's not a
|
||||
blocker, but it would definitely be useful. We're looking into it.
|
||||
|
||||
* The arm64 version doesn't support augeas, so it was built with:
|
||||
GOTAGS='noaugeas' to get the build out.
|
||||
|
||||
* We don't have the <|> operator merged yet. Expect that when we do this, we'll
|
||||
consider removing the || (default) operator. This is the only pending language
|
||||
change that I know of, and these cases are easily caught by the compiler and can
|
||||
be easily patched.
|
||||
|
||||
* Autoedge performance isn't great. It can easily be disabled. Most of the time
|
||||
I just specify my edges, so this is really a convenience feature, but it should
|
||||
be looked into when we have a chance.
|
||||
|
||||
* There's a subtle ssh reconnect issue which can occur. It should be easy to
|
||||
fix and I have a patch in testing.
|
||||
|
||||
* Our wasm code input fields grew tick marks, but I think this disturbed the
|
||||
buggy wasm code. If someone is an expert here, please have at it.
|
||||
|
||||
|
||||
TALKS
|
||||
|
||||
After FOSDEM/CfgMgmtCamp 2026, I don't have anything planned until CfgMgmtCamp
|
||||
2027. If you'd like to book me for a private event, or sponsor my travel for
|
||||
your conference, please let me know.
|
||||
|
||||
|
||||
MISC
|
||||
|
||||
Our mailing list host (Red Hat) is no longer letting non-Red Hat employees use
|
||||
their infrastructure. We're looking for a new home. I've opened a ticket with
|
||||
Freedesktop. If you have any sway with them or other recommendations, please let
|
||||
me know:
|
||||
https://gitlab.freedesktop.org/freedesktop/freedesktop/-/issues/1082
|
||||
|
||||
We're still looking for new contributors, and while there are easy, medium and
|
||||
hard issues available! You're also welcome to suggest your own! Please join us
|
||||
in #mgmtconfig on Libera IRC or Matrix (preferred) and ping us if you'd like
|
||||
help getting started! For details please see:
|
||||
|
||||
https://github.com/purpleidea/mgmt/blob/master/docs/faq.md#how-do-i-contribute-to-the-project-if-i-dont-know-golang
|
||||
|
||||
Many tagged #mgmtlove issues exist:
|
||||
https://github.com/purpleidea/mgmt/issues?q=is%3Aissue+is%3Aopen+label%3Amgmtlove
|
||||
|
||||
Although asking in matrix is the best way to find something to work on.
|
||||
|
||||
|
||||
MENTORING
|
||||
|
||||
We offer mentoring for new golang/mgmt hackers who want to get involved. This is
|
||||
fun and friendly! You get to improve your skills, and we get some patches in
|
||||
return. Ping me off-list for details.
|
||||
|
||||
|
||||
THANKS
|
||||
|
||||
Thanks (alphabetically) to everyone who contributed to the latest release:
|
||||
Ahmad Abuziad, Edward Toroshchyn, Felix Frank, hades, James Shubin, Karpfen, Lourenço, Lourenço Vales, Samuel Gélineau
|
||||
We had 10 unique committers since 0.0.27, and have had 103 overall.
|
||||
run 'git log 0.0.27..1.0.0' to see what has changed since 0.0.27
|
||||
|
||||
|
||||
Happy hacking,
|
||||
James
|
||||
@purpleidea
|
||||
826
docs/resource-guide.md
Normal file
826
docs/resource-guide.md
Normal file
@@ -0,0 +1,826 @@
|
||||
# Resource guide
|
||||
|
||||
## Overview
|
||||
|
||||
The `mgmt` tool has built-in resource primitives which make up the building
|
||||
blocks of any configuration. Each instance of a resource is mapped to a single
|
||||
vertex in the resource [graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph).
|
||||
This guide is meant to instruct developers on how to write a brand new resource.
|
||||
Since `mgmt` and the core resources are written in golang, some prior golang
|
||||
knowledge is assumed.
|
||||
|
||||
## Theory
|
||||
|
||||
Resources in `mgmt` are similar to resources in other systems in that they are
|
||||
[idempotent](https://en.wikipedia.org/wiki/Idempotence). Our resources are
|
||||
uniquely different in that they can detect when their state has changed, and as
|
||||
a result can run to revert or repair this change instantly. For some background
|
||||
on this design, please read the
|
||||
[original article](https://purpleidea.com/blog/2016/01/18/next-generation-configuration-mgmt/)
|
||||
on the subject.
|
||||
|
||||
## Resource Prerequisites
|
||||
|
||||
### Imports
|
||||
|
||||
You'll need to import a few packages to make writing your resource easier. Here
|
||||
is the list:
|
||||
|
||||
```
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
"github.com/purpleidea/mgmt/engine/traits"
|
||||
```
|
||||
|
||||
The `engine` package contains most of the interfaces and helper functions that
|
||||
you'll need to use. The `traits` package contains some base functionality which
|
||||
you can use to easily add functionality to your resource without needing to
|
||||
implement it from scratch.
|
||||
|
||||
### Resource struct
|
||||
|
||||
Each resource will implement methods as pointer receivers on a resource struct.
|
||||
The naming convention for resources is that they end with a `Res` suffix.
|
||||
|
||||
The resource struct should include an anonymous reference to the `Base` trait.
|
||||
Other `traits` can be added to the resource to add additional functionality.
|
||||
They are discussed below.
|
||||
|
||||
You'll most likely want to store a reference to the `*Init` struct type as
|
||||
defined by the engine. This is data that the engine will provide to your
|
||||
resource on Init.
|
||||
|
||||
Lastly you should define the public fields that make up your resource API, as
|
||||
well as any private fields that you might want to use throughout your resource.
|
||||
Do _not_ depend on global variables, since multiple copies of your resource
|
||||
could get instantiated.
|
||||
|
||||
You'll want to add struct tags based on the different frontends that you want
|
||||
your resources to be able to use. Some frontends can infer this information if
|
||||
it is not specified, but others cannot, and some might poorly infer if the
|
||||
struct name is ambiguous.
|
||||
|
||||
If you'd like your resource to be accessible by the `YAML` graph API (GAPI),
|
||||
then you'll need to include the appropriate YAML fields as shown below. This is
|
||||
used by the `puppet` compiler as well, so make sure you include these struct
|
||||
tags if you want existing `puppet` code to be able to run using the `mgmt`
|
||||
engine.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
type FooRes struct {
|
||||
traits.Base // add the base methods without re-implementation
|
||||
traits.Groupable
|
||||
traits.Refreshable
|
||||
|
||||
init *engine.Init
|
||||
|
||||
Whatever string `lang:"whatever" yaml:"whatever"` // you pick!
|
||||
Baz bool `lang:"baz" yaml:"baz"` // something else
|
||||
|
||||
something string // some private field
|
||||
}
|
||||
```
|
||||
|
||||
## Resource API
|
||||
|
||||
To implement a resource in `mgmt` it must satisfy the
|
||||
[`Res`](https://github.com/purpleidea/mgmt/blob/master/engine/resources.go)
|
||||
interface. What follows are each of the method signatures and a description of
|
||||
each.
|
||||
|
||||
### Default
|
||||
|
||||
```golang
|
||||
Default() engine.Res
|
||||
```
|
||||
|
||||
This returns a populated resource struct as a `Res`. It shouldn't populate any
|
||||
values which already get a good default as the respective golang zero value. In
|
||||
general it is preferable if the zero values make for the correct defaults.
|
||||
(This is to say, resources are designed to behave safely and intuitively
|
||||
when parameters take a zero value, whenever this is possible.)
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Default returns some sensible defaults for this resource.
|
||||
func (obj *FooRes) Default() engine.Res {
|
||||
return &FooRes{
|
||||
Answer: 42, // sometimes, defaults shouldn't be the zero value
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Validate
|
||||
|
||||
```golang
|
||||
Validate() error
|
||||
```
|
||||
|
||||
This method is used to validate if the populated resource struct is a valid
|
||||
representation of the resource kind. If it does not conform to the resource
|
||||
specifications, it should return an error. If you notice that this method is
|
||||
quite large, it might be an indication that you should reconsider the parameter
|
||||
list and interface to this resource. This method is called by the engine
|
||||
_before_ `Init`. It can also be called occasionally after a Send/Recv operation
|
||||
to verify that the newly populated parameters are valid. Remember not to expect
|
||||
access to the outside world when using this.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Validate reports any problems with the struct definition.
|
||||
func (obj *FooRes) Validate() error {
|
||||
if obj.Answer != 42 { // validate whatever you want
|
||||
return fmt.Errorf("expected an answer of 42")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Init
|
||||
|
||||
```golang
|
||||
Init() error
|
||||
```
|
||||
|
||||
This is called to initialize the resource. If something goes wrong, it should
|
||||
return an error. It should do any resource specific work such as initializing
|
||||
channels, sync primitives, or anything else that is relevant to your resource.
|
||||
If it is not need throughout, it might be preferable to do some initialization
|
||||
and tear down locally in either the Watch method or CheckApply method. The
|
||||
choice depends on your particular resource and making the best decision requires
|
||||
some experience with mgmt. If you are unsure, feel free to ask an existing
|
||||
`mgmt` contributor. During `Init`, the engine will pass your resource a struct
|
||||
containing some useful data and pointers. You should save a copy of this pointer
|
||||
since you will need to use it in other parts of your resource.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Init initializes the Foo resource.
|
||||
func (obj *FooRes) Init(init *engine.Init) error
|
||||
obj.init = init // save for later
|
||||
|
||||
// run the resource specific initialization, and error if anything fails
|
||||
if some_error {
|
||||
return err // something went wrong!
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
This method is always called after `Validate` has run successfully, with the
|
||||
exception that we can't prevent a malicious or buggy `libmgmt` user to not run
|
||||
this. In other words, you should expect `Validate` to have run first, but you
|
||||
shouldn't allow `Init` to dangerously `rm -rf /$the_world` if your code only
|
||||
checks `$the_world` in `Validate`. Remember to always program safely!
|
||||
|
||||
### Cleanup
|
||||
|
||||
```golang
|
||||
Cleanup() error
|
||||
```
|
||||
|
||||
This is called to cleanup after the resource. It is usually not necessary, but
|
||||
can be useful if you'd like to properly close a persistent connection that you
|
||||
opened in the `Init` method and were using throughout the resource. It is *not*
|
||||
the shutdown signal that tells the resource to exit. That happens in the Watch
|
||||
loop.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Cleanup is run by the engine to clean up after the resource is done.
|
||||
func (obj *FooRes) Cleanup() error {
|
||||
err := obj.conn.Close() // close some internal connection
|
||||
obj.someMap = nil // free up some large data structure from memory
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
You should probably check the return errors of your internal methods, and pass
|
||||
on an error if something went wrong.
|
||||
|
||||
### CheckApply
|
||||
|
||||
```golang
|
||||
CheckApply(ctx context.Context, apply bool) (checkOK bool, err error)
|
||||
```
|
||||
|
||||
`CheckApply` is where the real _work_ is done. Under normal circumstances, this
|
||||
function should check if the state of this resource is correct, and if so, it
|
||||
should return: `(true, nil)`. If the `apply` variable is set to `true`, then
|
||||
this means that we should then proceed to run the changes required to bring the
|
||||
resource into the correct state. If the `apply` variable is set to `false`, then
|
||||
the resource is operating in _noop_ mode and _no operational changes_ should be
|
||||
made! The ctx should be monitored in case a shutdown has been requested. This
|
||||
may be used if a timeout occurred, or if the user shutdown the engine.
|
||||
|
||||
After having executed the necessary operations to bring the resource back into
|
||||
the desired state, or after having detected that the state was incorrect, but
|
||||
that changes can't be made because `apply` is `false`, you should then return
|
||||
`(false, nil)`.
|
||||
|
||||
You must cause the resource to converge during a single execution of this
|
||||
function. If you cannot, then you must return an error! The exception to this
|
||||
rule is that if an external force changes the state of the resource while it is
|
||||
being remedied, it is possible to return from this function even though the
|
||||
resource isn't now converged. This is not a bug, as the resources `Watch`
|
||||
facility will detect the new change, ultimately resulting in a subsequent call
|
||||
to `CheckApply`.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// CheckApply does the idempotent work of checking and applying resource state.
|
||||
func (obj *FooRes) CheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
// check the state
|
||||
if state_is_okay { return true, nil } // done early! :)
|
||||
|
||||
// state was bad
|
||||
|
||||
if !apply { return false, nil } // don't apply, we're in noop mode
|
||||
|
||||
if any_error { return false, err } // anytime there's an err!
|
||||
|
||||
// do the apply!
|
||||
return false, nil // after success applying
|
||||
}
|
||||
```
|
||||
|
||||
The `CheckApply` function is called by the `mgmt` engine when it believes a call
|
||||
is necessary. Under certain conditions when a `Watch` call does not invalidate
|
||||
the state of the resource, and no refresh call was sent, its execution might be
|
||||
skipped. This is an engine optimization, and not a bug. It is mentioned here in
|
||||
the documentation in case you are confused as to why a debug message you've
|
||||
added to the code isn't always printed.
|
||||
|
||||
#### Paired execution
|
||||
|
||||
For many resources it is not uncommon to see `CheckApply` run twice in rapid
|
||||
succession. This is usually not a pathological occurrence, but rather a healthy
|
||||
pattern which is a consequence of the event system. When the state of the
|
||||
resource is incorrect, `CheckApply` will run to remedy the state. In response to
|
||||
having just changed the state, it is usually the case that this repair will
|
||||
trigger the `Watch` code! In response, a second `CheckApply` is triggered, which
|
||||
will likely find the state to now be correct.
|
||||
|
||||
#### Summary
|
||||
|
||||
* Anytime an error occurs during `CheckApply`, you should return `(false, err)`.
|
||||
* If the state is correct and no changes are needed, return `(true, nil)`.
|
||||
* You should only make changes to the system if `apply` is set to `true`.
|
||||
* After checking the state and possibly applying the fix, return `(false, nil)`.
|
||||
* Returning `(true, err)` is a programming error and can have a negative effect.
|
||||
|
||||
### Watch
|
||||
|
||||
```golang
|
||||
Watch(ctx context.Context) error
|
||||
```
|
||||
|
||||
`Watch` is a main loop that runs and sends messages when it detects that the
|
||||
state of the resource might have changed. To send a message you should write to
|
||||
the input event channel using the `Event` helper method. The Watch function
|
||||
should run continuously until a shutdown message is received. If at any time
|
||||
something goes wrong, you should return an error, and the `mgmt` engine will
|
||||
handle possibly restarting the main loop based on the `retry` meta parameter.
|
||||
|
||||
It is better to send an event notification which turns out to be spurious, than
|
||||
to miss a possible event. Resources which can miss events are incorrect and need
|
||||
to be re-engineered so that this isn't the case. If you have an idea for a
|
||||
resource which would fit this criteria, but you can't find a solution, please
|
||||
contact the `mgmt` maintainers so that this problem can be investigated and a
|
||||
possible system level engineering fix can be found.
|
||||
|
||||
You may have trouble deciding how much resource state checking should happen in
|
||||
the `Watch` loop versus deferring it all to the `CheckApply` method. You may
|
||||
want to put some simple fast path checking in `Watch` to avoid generating
|
||||
obviously spurious events, but in general it's best to keep the `Watch` method
|
||||
as simple as possible. Contact the `mgmt` maintainers if you're not sure.
|
||||
|
||||
If the resource is activated in `polling` mode, the `Watch` method will not get
|
||||
executed. As a result, the resource must still work even if the main loop is not
|
||||
running.
|
||||
|
||||
You must make sure to cleanup any running code or goroutines before Watch exits.
|
||||
|
||||
#### Select
|
||||
|
||||
The lifetime of most resources `Watch` method should be spent in an infinite
|
||||
loop that is bounded by a `select` call. The `select` call is the point where
|
||||
our method hands back control to the engine (and the kernel) so that we can
|
||||
sleep until something of interest wakes us up. In this loop we must wait until
|
||||
we get a shutdown event from the engine via the `<-ctx.Done()` channel, which
|
||||
closes when we'd like to shut everything down. At this point you should cleanup,
|
||||
and let `Watch` close.
|
||||
|
||||
#### Events
|
||||
|
||||
If the `<-ctx.Done()` channel closes, we should shutdown our resource. When we
|
||||
want to send an event, we use the `Event` helper function. This automatically
|
||||
marks the resource state as `dirty`. If you're unsure, it's not harmful to send
|
||||
the event. This will ultimately cause `CheckApply` to run. This method can block
|
||||
if the resource is being paused.
|
||||
|
||||
#### Startup
|
||||
|
||||
Once the `Watch` function has finished starting up successfully, it is important
|
||||
to generate one event to notify the `mgmt` engine that we're now listening
|
||||
successfully, so that it can run an initial `CheckApply` to ensure we're safely
|
||||
tracking a healthy state and that we didn't miss anything when `Watch` was down
|
||||
or from before `mgmt` was running. You must do this by calling the
|
||||
`obj.init.Running` method.
|
||||
|
||||
#### Converged
|
||||
|
||||
The engine might be asked to shutdown when the entire state of the system has
|
||||
not seen any changes for some duration of time. The engine can determine this
|
||||
automatically, but each resource can block this if it is absolutely necessary.
|
||||
If you need this functionality, please contact one of the maintainers and ask
|
||||
about adding this feature and improving these docs right here.
|
||||
|
||||
This particular facility is most likely not required for most resources. It may
|
||||
prove to be useful if a resource wants to start off a long operation, but avoid
|
||||
sending out erroneous `Event` messages to keep things alive until it finishes.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Watch is the listener and main loop for this resource.
|
||||
func (obj *FooRes) Watch(ctx context.Context) error {
|
||||
// setup the Foo resource
|
||||
var err error
|
||||
if err, obj.foo = OpenFoo(); err != nil {
|
||||
return err // we couldn't startup
|
||||
}
|
||||
defer obj.whatever.CloseFoo() // shutdown our Foo
|
||||
|
||||
// notify engine that we're running
|
||||
obj.init.Running() // when started, notify engine that we're running
|
||||
|
||||
for {
|
||||
select {
|
||||
// the actual events!
|
||||
case event := <-obj.foo.Events:
|
||||
if !is_an_event {
|
||||
continue // skip event
|
||||
}
|
||||
// send below...
|
||||
|
||||
// event errors
|
||||
case err := <-obj.foo.Errors:
|
||||
return err // will cause a retry or permanent failure
|
||||
|
||||
case <-ctx.Done(): // signal for shutdown request
|
||||
return nil
|
||||
}
|
||||
|
||||
obj.init.Event() // notify engine of an event (this can block)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Summary
|
||||
|
||||
* Remember to call `Running` when the `Watch` is running successfully.
|
||||
* Remember to process internal events and shutdown promptly if asked to.
|
||||
* Ensure the design of your resource is well thought out.
|
||||
* Have a look at the existing resources for a rough idea of how this all works.
|
||||
|
||||
### Cmp
|
||||
|
||||
```golang
|
||||
Cmp(engine.Res) error
|
||||
```
|
||||
|
||||
Each resource must have a `Cmp` method. It is an abbreviation for `Compare`. It
|
||||
takes as input another resource and must return whether they are identical or
|
||||
not. This is used for identifying if an existing resource can be used in place
|
||||
of a new one with a similar set of parameters. In particular, when switching
|
||||
from one graph to a new (possibly identical) graph, this avoids recomputing the
|
||||
state for resources which don't change or that are sufficiently similar that
|
||||
they don't need to be swapped out.
|
||||
|
||||
In general if all the resource properties are identical, then they usually don't
|
||||
need to be changed. On occasion, not all of them need to be compared, in
|
||||
particular if they store some generated state, or if they aren't significant in
|
||||
some way.
|
||||
|
||||
If the resource is identical, then you should return `nil`. If it is not, then
|
||||
you should return a short error message which gives the reason it differs.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Cmp compares two resources and returns if they are equivalent.
|
||||
func (obj *FooRes) Cmp(r engine.Res) error {
|
||||
// we can only compare FooRes to others of the same resource kind
|
||||
res, ok := r.(*FooRes)
|
||||
if !ok {
|
||||
return fmt.Errorf("not a %s", obj.Kind())
|
||||
}
|
||||
|
||||
if obj.Whatever != res.Whatever {
|
||||
return fmt.Errorf("the Whatever param differs")
|
||||
}
|
||||
if obj.Flag != res.Flag {
|
||||
return fmt.Errorf("the Flag param differs")
|
||||
}
|
||||
|
||||
return nil // they must match!
|
||||
}
|
||||
```
|
||||
|
||||
## Traits
|
||||
|
||||
Resources can have different `traits`, which means they can be extended to have
|
||||
additional functionality or special properties. Those special properties are
|
||||
usually added by extending your resource so that it is compatible with
|
||||
additional interface that contain the `Res` interface. Each of these interfaces
|
||||
represents the additional functionality. Since in most cases this requires some
|
||||
common boilerplate, you can usually get some or most of the functionality by
|
||||
embedding the correct trait struct anonymously in your struct. This is shown in
|
||||
the struct example above. You'll always want to include the `Base` trait in all
|
||||
resources. This provides some basics which you'll always need.
|
||||
|
||||
What follows are a list of available traits.
|
||||
|
||||
### Refreshable
|
||||
|
||||
Some resources may choose to support receiving refresh notifications. In general
|
||||
these should be avoided if possible, but nevertheless, they do make sense in
|
||||
certain situations. Resources that support these need to verify if one was sent
|
||||
during the CheckApply phase of execution. This is accomplished by calling the
|
||||
`obj.init.Refresh() bool` method, and inspecting the return value. This is only
|
||||
necessary if you plan to perform a refresh action. Refresh actions should still
|
||||
respect the `apply` variable, and no system changes should be made if it is
|
||||
`false`. Refresh notifications are generated by any resource when an action is
|
||||
applied by that resource and are transmitted through graph edges which have
|
||||
enabled their propagation. Resources that currently perform some refresh action
|
||||
include `svc`, `timer`, and `password`.
|
||||
|
||||
It is very important that you include the `traits.Refreshable` struct in your
|
||||
resource. If you do not include this, then calling `obj.init.Refresh` may
|
||||
trigger a panic. This is programmer error.
|
||||
|
||||
### Edgeable
|
||||
|
||||
Edgeable is a trait that allows your resource to automatically connect itself to
|
||||
other resources that use this trait to add edge dependencies between the two. An
|
||||
older blog post on this topic is
|
||||
[available](https://purpleidea.com/blog/2016/03/14/automatic-edges-in-mgmt/).
|
||||
|
||||
After you've included this trait, you'll need to implement two methods on your
|
||||
resource.
|
||||
|
||||
#### UIDs
|
||||
|
||||
```golang
|
||||
UIDs() []engine.ResUID
|
||||
```
|
||||
|
||||
The `UIDs` method returns a list of `ResUID` interfaces that represent the
|
||||
particular resource uniquely. This is used with the AutoEdges API to determine
|
||||
if another resource can match a dependency to this one.
|
||||
|
||||
#### AutoEdges
|
||||
|
||||
```golang
|
||||
AutoEdges() (engine.AutoEdge, error)
|
||||
```
|
||||
|
||||
This returns a struct that implements the `AutoEdge` interface. This struct
|
||||
is used to match other resources that might be relevant dependencies for this
|
||||
resource.
|
||||
|
||||
### Groupable
|
||||
|
||||
Groupable is a trait that can allow your resource automatically group itself to
|
||||
other resources. Doing so can reduce the resource or runtime burden on the
|
||||
engine, and improve performance in some scenarios. An older blog post on this
|
||||
topic is
|
||||
[available](https://purpleidea.com/blog/2016/03/30/automatic-grouping-in-mgmt/).
|
||||
|
||||
### Sendable
|
||||
|
||||
Sendable is a trait that allows your resource to send values through the graph
|
||||
edges to another resource. These values are produced during `CheckApply`. They
|
||||
can be sent to any resource that has an appropriate parameter and that has the
|
||||
`Recvable` trait. You can read more about this in the Send/Recv section below.
|
||||
|
||||
### Recvable
|
||||
|
||||
Recvable is a trait that allows your resource to receive values through the
|
||||
graph edges from another resource. These values are consumed during the
|
||||
`CheckApply` phase, and can be detected there as well. They can be received from
|
||||
any resource that has an appropriate value and that has the `Sendable` trait.
|
||||
You can read more about this in the Send/Recv section below.
|
||||
|
||||
### Exportable
|
||||
|
||||
Exportable allows a resource to tell the exporter what subset of its data it
|
||||
wishes to export when that occurs. It is rare that you will need to use this.
|
||||
|
||||
## Resource Initialization
|
||||
|
||||
During the resource initialization in `Init`, the engine will pass in a struct
|
||||
containing a bunch of data and methods. What follows is a description of each
|
||||
one and how it is used.
|
||||
|
||||
### Program
|
||||
|
||||
Program is a string containing the name of the program. Very few resources need
|
||||
this.
|
||||
|
||||
### Hostname
|
||||
|
||||
Hostname is the uuid for the host. It will be occasionally useful in some
|
||||
resources. It is preferable if you can avoid depending on this. It is possible
|
||||
that in the future this will be a channel which changes if the local hostname
|
||||
changes.
|
||||
|
||||
### Running
|
||||
|
||||
Running must be called after your watches are all started and ready. It is only
|
||||
called from within `Watch`. It is used to notify the engine that you're now
|
||||
ready to detect changes.
|
||||
|
||||
### Event
|
||||
|
||||
Event sends an event notifying the engine of a possible state change. It is
|
||||
only called from within `Watch`.
|
||||
|
||||
### Refresh
|
||||
|
||||
Refresh returns whether the resource received a notification. This flag can be
|
||||
used to tell a `svc` to reload, or to perform some state change that wouldn't
|
||||
otherwise be noticed by inspection alone. You must implement the `Refreshable`
|
||||
trait for this to work. It is only called from within `CheckApply`.
|
||||
|
||||
### Send
|
||||
|
||||
Send exposes some variables you wish to send via the `Send/Recv` mechanism. You
|
||||
must implement the `Sendable` trait for this to work. It is only called from
|
||||
within `CheckApply`.
|
||||
|
||||
### Recv
|
||||
|
||||
Recv provides a map of variables which were sent to this resource via the
|
||||
`Send/Recv` mechanism. You must implement the `Recvable` trait for this to work.
|
||||
It is only called from within `CheckApply`.
|
||||
|
||||
### World
|
||||
|
||||
World provides a connection to the outside world. This is most often used for
|
||||
communicating with the distributed database. It can be used in `Init`,
|
||||
`CheckApply` and `Watch`. Use with discretion and understanding of the internals
|
||||
if needed in `Cleanup`.
|
||||
|
||||
### VarDir
|
||||
|
||||
VarDir is a facility for local storage. It is used to return a path to a
|
||||
directory which may be used for temporary storage. It should be cleaned up on
|
||||
resource `Cleanup` if the resource would like to delete the contents. The
|
||||
resource should not assume that the initial directory is empty, and it should be
|
||||
cleaned on `Init` if that is a requirement.
|
||||
|
||||
### Debug
|
||||
|
||||
Debug signals whether we are running in debugging mode. In this case, we might
|
||||
want to log additional messages.
|
||||
|
||||
### Logf
|
||||
|
||||
Logf is a logging facility which will correctly namespace any messages which you
|
||||
wish to pass on. You should use this instead of the log package directly for
|
||||
production quality resources.
|
||||
|
||||
## Further considerations
|
||||
|
||||
There is some additional information that any resource writer will need to know.
|
||||
Each issue is listed separately below!
|
||||
|
||||
### Resource registration
|
||||
|
||||
All resources must be registered with the engine so that they can be found. This
|
||||
also ensures they can be encoded and decoded. Make sure to include the following
|
||||
code snippet for this to work.
|
||||
|
||||
```golang
|
||||
func init() { // special golang method that runs once
|
||||
// set your resource kind and struct here (the kind must be lower case)
|
||||
engine.RegisterResource("foo", func() engine.Res { return &FooRes{} })
|
||||
}
|
||||
```
|
||||
|
||||
### YAML Unmarshalling
|
||||
|
||||
To support YAML unmarshalling for your resource, you must implement an
|
||||
additional method. It is recommended if you want to use your resource with the
|
||||
`puppet` compiler.
|
||||
|
||||
```golang
|
||||
UnmarshalYAML(unmarshal func(interface{}) error) error // optional
|
||||
```
|
||||
|
||||
This is optional, but recommended for any resource that will have a YAML
|
||||
accessible struct. It is not required because to do so would mean that
|
||||
third-party or custom resources (such as those someone writes to use with
|
||||
`libmgmt`) would have to implement this needlessly.
|
||||
|
||||
The signature intentionally matches what is required to satisfy the `go-yaml`
|
||||
[Unmarshaler](https://godoc.org/gopkg.in/yaml.v2#Unmarshaler) interface.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// UnmarshalYAML is the custom unmarshal handler for this struct. It is
|
||||
// primarily useful for setting the defaults.
|
||||
func (obj *FooRes) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
type rawRes FooRes // indirection to avoid infinite recursion
|
||||
|
||||
def := obj.Default() // get the default
|
||||
res, ok := def.(*FooRes) // put in the right format
|
||||
if !ok {
|
||||
return fmt.Errorf("could not convert to FooRes")
|
||||
}
|
||||
raw := rawRes(*res) // convert; the defaults go here
|
||||
|
||||
if err := unmarshal(&raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*obj = FooRes(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Send/Recv
|
||||
|
||||
In `mgmt` there is a novel concept called _Send/Recv_. For some background,
|
||||
please read the [introductory article](https://purpleidea.com/blog/2016/12/07/sendrecv-in-mgmt/).
|
||||
When using this feature, the engine will automatically send the user specified
|
||||
value to the intended destination without requiring much resource specific code.
|
||||
Any time that one of the destination values is changed, the engine automatically
|
||||
marks the resource state as `dirty`. To detect if a particular value was
|
||||
received, and if it changed (during this invocation of `CheckApply`) from the
|
||||
previous value, you can query the `obj.init.Recv()` method. It will contain a
|
||||
`map` of all the keys which can be received on, and the value has a `Changed`
|
||||
property which will indicate whether the value was updated on this particular
|
||||
`CheckApply` invocation. The type of the sending key must match that of the
|
||||
receiving one. This can _only_ be done inside of the `CheckApply` function!
|
||||
|
||||
```golang
|
||||
// inside CheckApply, probably near the top
|
||||
if val, exists := obj.init.Recv()["some_key"]; exists {
|
||||
obj.init.Logf("the some_key param was sent to us from: %s.%s", val.Res, val.Key)
|
||||
if val.Changed {
|
||||
obj.init.Logf("the some_key param was just updated!")
|
||||
// you may want to invalidate some local cache
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A resource can send a value during CheckApply by running the `obj.init.Send()`
|
||||
method. It must always send a value if (1) it's not erroring in CheckApply, and
|
||||
(2) if the `obj.SendActive()` method inside of CheckApply returns true. It is
|
||||
not harmful to run the Send method if CheckApply is going to error, or if
|
||||
`obj.SendActive()` returns false, just unnecessary. In the `!apply` case where
|
||||
we're running in "noop" mode, and where the state is not correct, then you
|
||||
should still attempt to send a value, but it is a bit ambiguous which value to
|
||||
send. This behaviour may be specified in the future, but at the moment it's
|
||||
mostly inconsequential. At the moment, `obj.SendActive()` is disabled at compile
|
||||
time, but can be enabled if you have a legitimate use-case for it.
|
||||
|
||||
```golang
|
||||
// inside CheckApply, somewhere near the end usually
|
||||
if err := obj.init.Send(&ExecSends{ // send the special data structure
|
||||
Output: obj.output,
|
||||
Stdout: obj.stdout,
|
||||
Stderr: obj.stderr,
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
```
|
||||
|
||||
You must also implement the `Sends()` method which should return the above
|
||||
sending struct with all of the fields containing their default or values. Please
|
||||
note, that those fields must have their struct tags set appropriately.
|
||||
|
||||
### Safety
|
||||
|
||||
Lastly, please note that in order for a resource to send a useful value, even
|
||||
when its state is already correct (it may have run earlier for example) then it
|
||||
may require the implementation of CheckApply to cache a return value for later
|
||||
use. Keep in mind that you should store this securely should there be a chance
|
||||
that sensitive info is contained within, and that an untrusted user could put
|
||||
malicious data in the cache if you are not careful. It's best to make sure the
|
||||
users of your resource are aware of its implementation details here.
|
||||
|
||||
## Composite resources
|
||||
|
||||
Composite resources are resources which embed one or more existing resources.
|
||||
This is useful to prevent code duplication in higher level resource scenarios.
|
||||
The best example of this technique can be seen in the `nspawn` resource which
|
||||
can be seen to partially embed a `svc` resource, but without its `Watch`.
|
||||
Unfortunately no further documentation about this subject has been written. To
|
||||
expand this section, please send a patch! Please contact us if you'd like to
|
||||
work on a resource that uses this feature, or to add it to an existing one!
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
(Send your questions as a patch to this FAQ! I'll review it, merge it, and
|
||||
respond by commit with the answer.)
|
||||
|
||||
### Can I write resources in a different language?
|
||||
|
||||
Currently `golang` is the only supported language for built-in resources. We
|
||||
might consider allowing external resources to be imported in the future. This
|
||||
will likely require a language that can expose a C-like API, such as `python` or
|
||||
`ruby`. Custom `golang` resources are already possible when using mgmt as a lib.
|
||||
Higher level resource collections will be possible once the `mgmt` DSL is ready.
|
||||
|
||||
### Why does the resource API have `CheckApply` instead of two separate methods?
|
||||
|
||||
In an early version we actually had both "parts" as separate methods, namely:
|
||||
`StateOK` (Check) and `Apply`, but the [decision](https://github.com/purpleidea/mgmt/commit/58f41eddd9c06b183f889f15d7c97af81b0331cc)
|
||||
was made to merge the two into a single method. There are two reasons for this:
|
||||
|
||||
1. Many situations would involve the engine running both `Check` and `Apply`. If
|
||||
the resource needed to share some state (for efficiency purposes) between the
|
||||
two calls, this is much more difficult. A common example is that a resource
|
||||
might want to open a connection to `dbus` or `http` to do resource state testing
|
||||
and applying. If the methods are combined, there's no need to open and close
|
||||
them twice. A counter argument might be that you could open the connection in
|
||||
`Init`, and close it in `Cleanup`, however you might not want that open for the
|
||||
full lifetime of the resource if you only change state occasionally.
|
||||
2. Suppose you came up with a really good reason why you wanted the two methods
|
||||
to be separate. It turns out that the current `CheckApply` can wrap this easily.
|
||||
It would look approximately like this:
|
||||
|
||||
```golang
|
||||
func (obj *FooRes) CheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
// my private split implementation of check and apply
|
||||
if c, err := obj.check(ctx); err != nil {
|
||||
return false, err // we errored
|
||||
} else if c {
|
||||
return true, nil // state was good!
|
||||
}
|
||||
|
||||
if !apply {
|
||||
return false, nil // state needs fixing, but apply is false
|
||||
}
|
||||
|
||||
err := obj.apply(ctx) // errors if failure or unable to apply
|
||||
|
||||
return false, err // always return false, with an optional error
|
||||
}
|
||||
```
|
||||
|
||||
Feel free to use this pattern if you're convinced it's necessary. Alternatively,
|
||||
if you think I got the `Res` API wrong and you have an improvement, please let
|
||||
us know!
|
||||
|
||||
### Why do resources have both a `Cmp` method and an `IFF` (on the UID) method?
|
||||
|
||||
The `Cmp()` methods are for determining if two resources are effectively the
|
||||
same, which is used to make graph change delta's efficient. This is when we want
|
||||
to change from the current running graph to a new graph, but preserve the common
|
||||
vertices. Since we want to make this process efficient, we only update the parts
|
||||
that are different, and leave everything else alone. This `Cmp()` method can
|
||||
tell us if two resources are the same. In case it is not obvious, `cmp` is an
|
||||
abbrev. for compare.
|
||||
|
||||
The `IFF()` method is part of the whole UID system, which is for discerning if a
|
||||
resource meets the requirements another expects for an automatic edge. This is
|
||||
because the automatic edge system assumes a unified UID pattern to test for
|
||||
equality. In the future it might be helpful or sane to merge the two similar
|
||||
comparison functions although for now they are separate because they are
|
||||
actually answer different questions.
|
||||
|
||||
### What new resource primitives need writing?
|
||||
|
||||
There are still many ideas for new resources that haven't been written yet. If
|
||||
you'd like to contribute one, please contact us and tell us about your idea!
|
||||
|
||||
### Is the resource API stable? Does it ever change?
|
||||
|
||||
Since we are pre 1.0, the resource API is not guaranteed to be stable, however
|
||||
it is not expected to change significantly. The last major change kept the
|
||||
core functionality nearly identical, simplified the implementation of all the
|
||||
resources, and took about five to ten minutes to port each resource to the new
|
||||
API. The fundamental logic and behaviour behind the resource API has not changed
|
||||
since it was initially introduced.
|
||||
|
||||
### Where can I find more information about mgmt?
|
||||
|
||||
Additional blog posts, videos and other material [is available!](https://github.com/purpleidea/mgmt/blob/master/docs/on-the-web.md).
|
||||
|
||||
## Suggestions
|
||||
|
||||
If you have any ideas for API changes or other improvements to resource writing,
|
||||
please let us know! We're still pre 1.0 and pre 0.1 and happy to break API in
|
||||
order to get it right!
|
||||
250
docs/resources.md
Normal file
250
docs/resources.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Resources
|
||||
|
||||
Here we list all the built-in resources and their properties. The resource
|
||||
primitives in `mgmt` are typically more powerful than resources in other
|
||||
configuration management systems because they can be event based which lets them
|
||||
respond in real-time to converge to the desired state. This property allows you
|
||||
to build more complex resources that you probably hadn't considered in the past.
|
||||
|
||||
In addition to the resource specific properties, there are resource properties
|
||||
(otherwise known as parameters) which can apply to every resource. These are
|
||||
called [meta parameters](documentation.md#meta-parameters) and are listed
|
||||
separately. Certain meta parameters aren't very useful when combined with
|
||||
certain resources, but in general, it should be fairly obvious, such as when
|
||||
combining the `noop` meta parameter with the [Noop](#Noop) resource.
|
||||
|
||||
You might want to look at the [generated documentation](https://godoc.org/github.com/purpleidea/mgmt/engine/resources)
|
||||
for more up-to-date information about these resources.
|
||||
|
||||
* [Augeas](#Augeas): Manipulate files using augeas.
|
||||
* [Consul:KV](#ConsulKV): Set keys in a Consul datastore.
|
||||
* [Docker](#Docker):[Container](#Container) Manage docker containers.
|
||||
* [Exec](#Exec): Execute shell commands on the system.
|
||||
* [File](#File): Manage files and directories.
|
||||
* [Group](#Group): Manage system groups.
|
||||
* [Hostname](#Hostname): Manages the hostname on the system.
|
||||
* [KV](#KV): Set a key value pair in our shared world database.
|
||||
* [Msg](#Msg): Send log messages.
|
||||
* [Net](#Net): Manage a local network interface.
|
||||
* [Noop](#Noop): A simple resource that does nothing.
|
||||
* [Nspawn](#Nspawn): Manage systemd-machined nspawn containers.
|
||||
* [Password](#Password): Create random password strings.
|
||||
* [Pkg](#Pkg): Manage system packages with PackageKit.
|
||||
* [Print](#Print): Print messages to the console.
|
||||
* [Svc](#Svc): Manage system systemd services.
|
||||
* [Test](#Test): A mostly harmless resource that is used for internal testing.
|
||||
* [Tftp:File](#TftpFile): Add files to the small embedded embedded tftp server.
|
||||
* [Tftp:Server](#TftpServer): Run a small embedded tftp server.
|
||||
* [Timer](#Timer): Manage system systemd services.
|
||||
* [User](#User): Manage system users.
|
||||
* [Virt](#Virt): Manage virtual machines with libvirt.
|
||||
|
||||
## Augeas
|
||||
|
||||
The augeas resource uses [augeas](http://augeas.net/) commands to manipulate
|
||||
files.
|
||||
|
||||
## Docker
|
||||
|
||||
### Container
|
||||
|
||||
The docker:container resource manages docker containers.
|
||||
|
||||
It has the following properties:
|
||||
|
||||
* `state`: either `running`, `stopped`, or `removed`
|
||||
* `image`: docker `image` or `image:tag`
|
||||
* `cmd`: a command or list of commands to run on the container
|
||||
* `env`: a list of environment variables, e.g. `["VAR=val",],`
|
||||
* `ports`: a map of portmappings, e.g. `{"tcp" => {8080 => 80, 8443 => 443,},},`
|
||||
* `apiversion:` override the host's default docker version, e.g. `"v1.35"`
|
||||
* `force`: destroy and rebuild the container instead of erroring on wrong image
|
||||
|
||||
## Exec
|
||||
|
||||
The exec resource can execute commands on your system.
|
||||
|
||||
## File
|
||||
|
||||
The file resource manages files and directories. In `mgmt`, directories are
|
||||
identified by a trailing slash in their path name. File have no such slash.
|
||||
|
||||
It has the following properties:
|
||||
|
||||
* `path`: absolute file path (directories have a trailing slash here)
|
||||
* `state`: either `exists`, `absent`, or undefined
|
||||
* `content`: raw file content
|
||||
* `mode`: octal unix file permissions or symbolic string
|
||||
* `owner`: username or uid for the file owner
|
||||
* `group`: group name or gid for the file group
|
||||
|
||||
### Path
|
||||
|
||||
The path property specifies the file or directory that we are managing.
|
||||
|
||||
### State
|
||||
|
||||
The state property describes the action we'd like to apply for the resource. The
|
||||
possible values are: `exists` and `absent`. If you do not specify either of
|
||||
these, it is undefined. Without specifying this value as `exists`, another param
|
||||
cannot cause a file to get implicitly created. When specifying this value as
|
||||
`absent`, you should not specify any other params that would normally change the
|
||||
file. For example, if you specify `content` and this param is `absent`, then you
|
||||
will get an engine validation error.
|
||||
|
||||
### Content
|
||||
|
||||
The content property is a string that specifies the desired file contents.
|
||||
|
||||
### Source
|
||||
|
||||
The source property points to a source file or directory path that we wish to
|
||||
copy over and use as the desired contents for our resource.
|
||||
|
||||
### Fragments
|
||||
|
||||
The fragments property lets you specify a list of files to concatenate together
|
||||
to make up the contents of this file. They will be combined in the order that
|
||||
they are listed in. If one of the files specified is a directory, then the
|
||||
files in that top-level directory will be themselves combined together and used.
|
||||
|
||||
### Recurse
|
||||
|
||||
The recurse property limits whether file resource operations should recurse into
|
||||
and monitor directory contents with a depth greater than one.
|
||||
|
||||
### Force
|
||||
|
||||
The force property is required if we want the file resource to be able to change
|
||||
a file into a directory or vice-versa. If such a change is needed, but the force
|
||||
property is not set to `true`, then this file resource will error.
|
||||
|
||||
### Purge
|
||||
|
||||
The purge property is used when this file represents a directory, and we'd like
|
||||
to remove any unmanaged files from within it. Please note that any unmanaged
|
||||
files in a directory with this flag set will be irreversibly deleted.
|
||||
|
||||
## Group
|
||||
|
||||
The group resource manages the system groups from `/etc/group`.
|
||||
|
||||
## Hostname
|
||||
|
||||
The hostname resource manages static, transient/dynamic and pretty hostnames
|
||||
on the system and watches them for changes.
|
||||
|
||||
### static_hostname
|
||||
|
||||
The static hostname is the one configured in /etc/hostname or a similar
|
||||
file.
|
||||
It is chosen by the local user. It is not always in sync with the current
|
||||
host name as returned by the gethostname() system call.
|
||||
|
||||
### transient_hostname
|
||||
|
||||
The transient / dynamic hostname is the one configured via the kernel's
|
||||
sethostbyname().
|
||||
It can be different from the static hostname in case DHCP or mDNS have been
|
||||
configured to change the name based on network information.
|
||||
|
||||
### pretty_hostname
|
||||
|
||||
The pretty hostname is a free-form UTF8 host name for presentation to the user.
|
||||
|
||||
### hostname
|
||||
|
||||
Hostname is the fallback value for all 3 fields above, if only `hostname` is
|
||||
specified, it will set all 3 fields to this value.
|
||||
|
||||
## KV
|
||||
|
||||
The KV resource sets a key and value pair in the global world database. This is
|
||||
quite useful for setting a flag after a number of resources have run. It will
|
||||
ignore database updates to the value that are greater in compare order than the
|
||||
requested key if the `SkipLessThan` parameter is set to true. If we receive a
|
||||
refresh, then the stored value will be reset to the requested value even if the
|
||||
stored value is greater.
|
||||
|
||||
### Key
|
||||
|
||||
The string key used to store the key.
|
||||
|
||||
### Value
|
||||
|
||||
The string value to set. This can also be set via Send/Recv.
|
||||
|
||||
### SkipLessThan
|
||||
|
||||
If this parameter is set to `true`, then it will ignore updating the value as
|
||||
long as the database versions are greater than the requested value. The compare
|
||||
operation used is based on the `SkipCmpStyle` parameter.
|
||||
|
||||
### SkipCmpStyle
|
||||
|
||||
By default this converts the string values to integers and compares them as you
|
||||
would expect.
|
||||
|
||||
## Msg
|
||||
|
||||
The msg resource sends messages to the main log, or an external service such
|
||||
as systemd's journal.
|
||||
|
||||
## Net
|
||||
|
||||
The net resource manages a local network interface using netlink.
|
||||
|
||||
## Noop
|
||||
|
||||
The noop resource does absolutely nothing. It does have some utility in testing
|
||||
`mgmt` and also as a placeholder in the resource graph.
|
||||
|
||||
## Nspawn
|
||||
|
||||
The nspawn resource is used to manage systemd-machined style containers.
|
||||
|
||||
## Password
|
||||
|
||||
The password resource can generate a random string to be used as a password. It
|
||||
will re-generate the password if it receives a refresh notification.
|
||||
|
||||
## Pkg
|
||||
|
||||
The pkg resource is used to manage system packages. This resource works on many
|
||||
different distributions because it uses the underlying packagekit facility which
|
||||
supports different backends for different environments. This ensures that we
|
||||
have great Debian (deb/dpkg) and Fedora (rpm/dnf) support simultaneously.
|
||||
|
||||
## Print
|
||||
|
||||
The print resource prints messages to the console.
|
||||
|
||||
## Svc
|
||||
|
||||
The service resource is still very WIP. Please help us by improving it!
|
||||
|
||||
## Test
|
||||
|
||||
The test resource is mostly harmless and is used for internal tests.
|
||||
|
||||
## Tftp:File
|
||||
|
||||
This adds files to the running tftp server. It's useful because it allows you to
|
||||
add individual files without needing to create them on disk.
|
||||
|
||||
## Tftp:Server
|
||||
|
||||
Run a small embedded tftp server. This doesn't apply any state, but instead runs
|
||||
a pure golang tftp server in the Watch loop.
|
||||
|
||||
## Timer
|
||||
|
||||
This resource needs better documentation. Please help us by improving it!
|
||||
|
||||
## User
|
||||
|
||||
The user resource manages the system users from `/etc/passwd`.
|
||||
|
||||
## Virt
|
||||
|
||||
The virt resource can manage virtual machines via libvirt.
|
||||
145
docs/service-guide.md
Normal file
145
docs/service-guide.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Service API design guide
|
||||
|
||||
This document is intended as a short instructional design guide in building a
|
||||
service management API. It is certainly intended for someone who wishes to use
|
||||
`mgmt` resources and functions to interact with their facilities, however it may
|
||||
be of more general use as well. Hopefully this will help you make smarter design
|
||||
considerations early on, and prevent some amount of unnecessary technical debt.
|
||||
|
||||
## Main aspects
|
||||
|
||||
What follows are some of the most common considerations which you may wish to
|
||||
take into account when building your service. This list is non-exhaustive. Of
|
||||
particular note, as of the writing of this document, many of these designs are
|
||||
not taken into account or not well-handled or implemented by the major API
|
||||
("cloud") providers.
|
||||
|
||||
### Authentication
|
||||
|
||||
#### The status-quo
|
||||
|
||||
Many services naturally require you to authenticate yourself. Usually the
|
||||
initial user who sets up the account and provides credit card details will need
|
||||
to download secret credentials in order to access the service. The onus is on
|
||||
the user to keep those credentials private, and to prevent leaking them. It is
|
||||
convenient (and insecure) to store them in `git` repositories containing scripts
|
||||
and configuration management code. Since it's likely you will use multiple
|
||||
different services, it also means you will have a ton of different credentials
|
||||
to guard.
|
||||
|
||||
#### An alternative
|
||||
|
||||
Instead, build your service to accept a public key that you store in the users
|
||||
account. Only consumers that can correctly sign messages matching this public
|
||||
key should be authorized. This mechanism is well-understood by anyone who has
|
||||
ever uploaded their public SSH key to a server. You can use SSH keys, GPG keys,
|
||||
or even get into Kerberos if that's appropriate. Best of all, if you and other
|
||||
services use a standardized mechanism like GPG, a user might only need to keep
|
||||
track of their single key-pair, even when they're using multiple services!
|
||||
|
||||
### Events
|
||||
|
||||
#### The problem
|
||||
|
||||
People have been building "[CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete)"
|
||||
and "[REST](https://en.wikipedia.org/wiki/REST)"ful API's for years. The biggest
|
||||
missing part that most of them don't provide is events. If users want to know
|
||||
when a resource changes, they have to repeatedly poll the server, which is both
|
||||
network intensive, and introduces latency. When services were simpler, this
|
||||
wasn't as much of a consideration, but these days it matters. An embarrassingly
|
||||
small number of major software vendors implement these correctly, if at all.
|
||||
|
||||
#### Why events?
|
||||
|
||||
The `mgmt` tool is different from most other static tools in that it allows
|
||||
reading streams of incoming data, and stream of change events from resources we
|
||||
are managing. If an event API is not available, we can still poll, but this is
|
||||
not as desirable. An event-capable API doesn't prevent polling if that's
|
||||
preferred, you can always repeat a read request periodically.
|
||||
|
||||
#### Variants
|
||||
|
||||
The two common mechanisms for receiving events are "callbacks" and
|
||||
"long-polling". In the former, the service contacts the consumer when something
|
||||
happens. In the latter, the consumer opens a connection, and the service either
|
||||
closes the connection or sends the reply, when it's ready. Long-polling is often
|
||||
preferred since it doesn't require an open firewall on the consumers side.
|
||||
Callbacks are preferred because it's often cheaper for the service to implement
|
||||
that. It's also less reliable since it's hard to know if the callback message
|
||||
wasn't received because it was dropped, or if there just wasn't an event. And it
|
||||
requires static timeouts when retrying a callback message, and so on. It's best
|
||||
to implement long-polling or something equivalent at a minimum.
|
||||
|
||||
#### "Since" requests
|
||||
|
||||
When making an event request, some API's will let you tack on a "since" style
|
||||
parameter that tells the endpoint that we're interested in all of the events
|
||||
_since_ a particular timestamp, or _since_ a particular sequence ID. This can be
|
||||
very useful if missing an intermediate event is a concern. Implement this if you
|
||||
can, but it's better for all concerned if purely declarative facilities are all
|
||||
that is required. It also forces the endpoint to maintain some state, which may
|
||||
be undesirable for them.
|
||||
|
||||
#### Out of band
|
||||
|
||||
Some providers have the event system tacked on to a separate facility. If it's
|
||||
not part of the core API, then it's not useful. You shouldn't have to configure
|
||||
a separate system in order to start getting events.
|
||||
|
||||
### Batching
|
||||
|
||||
With so many resources, you might expect to have 1000's of long-polling
|
||||
connections all sitting open and idle. That can't be efficient! It's not, which
|
||||
is why good API's need a batching facility. This lets the consumer group
|
||||
together many watches (all waiting on a long-poll) inside of a single call. That
|
||||
way, a single connection might only be needed for a large amount of information.
|
||||
|
||||
### Don't auto-generate junk
|
||||
|
||||
Please build an elegant API. Many services auto-generate a "phone book" SDK of
|
||||
junk. It might seem inevitable, so if you absolutely need to do this, then put
|
||||
some extra effort into making it idiomatic. If I'm using an SDK generated for
|
||||
`golang` and I see an internal `foo.String` wrapper, then chances are you have
|
||||
designed your API and code to be easier to maintain for you, instead of
|
||||
prioritizing your customers. Surely the total volume of all customer code is
|
||||
more than your own, so why optimize for that instead of the putting the customer
|
||||
first?
|
||||
|
||||
### Resources and functions
|
||||
|
||||
`Mgmt` has a concept of "resources" and "functions". Resources are used in an
|
||||
idempotent model to express desired state and perform that work, and "functions"
|
||||
are used to receive and pull data into the system. That separation has shown to
|
||||
be an elegant one. Consider it when designing your API's. For example, if some
|
||||
vital information can only be obtained after performing a modifying operation,
|
||||
then it might signal that you're missing some sort of a lookup or event-log
|
||||
system. Design your API's to be idempotent, this solves many distributed-system
|
||||
problems involving receiving duplicate messages, and so on.
|
||||
|
||||
## Using mgmt as a library
|
||||
|
||||
Instead of building a new service from scratch, and re-inventing the typical
|
||||
management and CLI layer, consider using `mgmt` as a library, and directly
|
||||
benefiting from that work. This has not been done for a large production
|
||||
service, but the author believes it would be quite efficient, particularly if
|
||||
your application is written in golang. It's equivalently easy to do it for other
|
||||
languages as well, you just end up with two binaries instead of one. (Or you can
|
||||
embed the other binary into the new golang management tool.)
|
||||
|
||||
## Cloud API considerations
|
||||
|
||||
Many "cloud" companies have a lot of technical debt and a lot of customers. As a
|
||||
result, it might be very hard for them to improve their API's, particularly
|
||||
without breaking compatibility promises for their existing customers. As a
|
||||
result, they should either add a versioned API, which lets newer consumers get
|
||||
the benefit, or add new parallel services which offer the modern features. If
|
||||
they don't, the only solution is for new competitors to build in these better
|
||||
efficiencies, eventually offering better value to cost ratios, which will then
|
||||
make legacy products less lucrative and therefore unmaintainable as compared to
|
||||
their competitors.
|
||||
|
||||
## Suggestions
|
||||
|
||||
If you have any ideas for suggestions or other improvements to this guide,
|
||||
please let us know! I hope this was helpful. Please reach out if you are
|
||||
building an API that you might like to have `mgmt` consume!
|
||||
283
docs/style-guide.md
Normal file
283
docs/style-guide.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# Style guide
|
||||
|
||||
This document aims to be a reference for the desired style for patches to mgmt,
|
||||
and the associated `mcl` language. In particular it describes conventions which
|
||||
are not officially enforced by tools and in test cases, or that aren't clearly
|
||||
defined elsewhere. We try to turn as many of these into automated tests as we
|
||||
can. If something here is not defined in a test, or you think it should be,
|
||||
please write one! Even better, you can write a tool to automatically fix it,
|
||||
since this is more useful and can easily be turned into a test!
|
||||
|
||||
## Overview for golang code
|
||||
|
||||
Most style issues are enforced by the `gofmt` tool. Other style aspects are
|
||||
often common sense to seasoned programmers, and we hope this will be a useful
|
||||
reference for new programmers.
|
||||
|
||||
There are a lot of useful code review comments described
|
||||
[here](https://github.com/golang/go/wiki/CodeReviewComments). We don't
|
||||
necessarily follow everything strictly, but it is in general a very good guide.
|
||||
|
||||
### Basics
|
||||
|
||||
* All of our golang code is formatted with `gofmt`.
|
||||
|
||||
### Comments
|
||||
|
||||
All of our code is commented with the minimums required for `godoc` to function,
|
||||
and so that our comments pass `golint`. Code comments should either be full
|
||||
sentences (which end with a period, use proper punctuation, and capitalize the
|
||||
first word when it is not a lower cased identifier), or are short one-line
|
||||
comments in the source which are not full sentences and don't end with a period.
|
||||
|
||||
They should explain algorithms, describe non-obvious behaviour, or situations
|
||||
which would otherwise need explanation or additional research during a code
|
||||
review. Notes about use of unfamiliar API's is a good idea for a code comment.
|
||||
|
||||
#### Example
|
||||
|
||||
Here you can see a function with the correct `godoc` string. The first word must
|
||||
match the name of the function. It is _not_ capitalized because the function is
|
||||
private.
|
||||
|
||||
```golang
|
||||
// square multiplies the input integer by itself and returns this product.
|
||||
func square(x int) int {
|
||||
return x * x // we don't care about overflow errors
|
||||
}
|
||||
```
|
||||
|
||||
### Line length
|
||||
|
||||
In general we try to stick to 80 character lines when it is appropriate. It is
|
||||
almost *always* appropriate for function `godoc` comments and most longer
|
||||
paragraphs. Exceptions are always allowed based on the will of the maintainer.
|
||||
|
||||
It is usually better to exceed 80 characters than to break code unnecessarily.
|
||||
If your code often exceeds 80 characters, it might be an indication that it
|
||||
needs refactoring.
|
||||
|
||||
Occasionally inline, two line source code comments are used within a function.
|
||||
These should usually be balanced so that you don't have one line with 78
|
||||
characters and the second with only four. Split the comment between the two.
|
||||
|
||||
### Default values
|
||||
|
||||
Whenever a constant or function parameter is defined, try and have the safer or
|
||||
default value be the `zero` value. For example, instead of `const NoDanger`, use
|
||||
`const AllowDanger` so that the `false` value is the safe scenario.
|
||||
|
||||
### Method receiver pointers
|
||||
|
||||
You almost always want any method receivers to be declared on the pointer to the
|
||||
struct. There are only a few rare situations where this is not the case. This
|
||||
makes it easier to merge future changes that mutate the state without wondering
|
||||
why you now have two different copies of a struct. When you do need to copy a
|
||||
a struct, you can add a `Copy()` method to it. It's true that in many situations
|
||||
adding the pointer adds a small performance penalty, but we haven't found them
|
||||
to be significant in practice. If you do have a performance sensitive patch
|
||||
which benefits from skipping the pointer, please demonstrate this need with
|
||||
data first.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
type Foo struct {
|
||||
Whatever string
|
||||
// ...
|
||||
}
|
||||
|
||||
// Bar is implemented correctly as a pointer on Foo.
|
||||
func (obj *Foo) Bar(baz string) int {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Bar is implemented *incorrectly* without a pointer to Foo.
|
||||
func (obj Foo) Bar(baz string) int {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Method receiver naming
|
||||
|
||||
[Contrary](https://github.com/golang/go/wiki/CodeReviewComments#receiver-names)
|
||||
to the specialized naming of the method receiver variable, we usually name all
|
||||
of these `obj` for ease of code copying throughout the project, and for faster
|
||||
identification when reviewing code. Some anecdotal studies have shown that it
|
||||
makes the code easier to read since you don't need to remember the name of the
|
||||
method receiver variable in each different method. This is very similar to what
|
||||
is done in `python`.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Bar does a thing, and returns the number of baz results found in our
|
||||
database.
|
||||
func (obj *Foo) Bar(baz string) int {
|
||||
if len(obj.s) > 0 {
|
||||
return strings.Count(obj.s, baz)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
```
|
||||
|
||||
### Variable naming
|
||||
|
||||
We prefer shorter, scoped variables rather than `unnecessarilyLongIdentifiers`.
|
||||
Remember the scoping rules and feel free to use new variables where appropriate.
|
||||
For example, in a short string snippet you can use `s` instead of `myString`, as
|
||||
well as other common choices. `i` is a common `int` counter, `f` for files, `fn`
|
||||
for functions, `x` for something else and so on.
|
||||
|
||||
### Variable reuse
|
||||
|
||||
Feel free to create and use new variables instead of attempting to reuse the
|
||||
same string. For example, if a function input arg is named `s`, you can use a
|
||||
new variable to receive the first computation result on `s` instead of storing
|
||||
it back into the original `s`. This avoids confusion if a different part of the
|
||||
code wants to read the original input, and it avoids any chance of edit by
|
||||
reference of the original callers copy of the variable.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
MyNotIdealFunc(s string, b bool) string {
|
||||
if !b {
|
||||
return s + "hey"
|
||||
}
|
||||
s = strings.Replace(s, "blah", "", -1) // not ideal (reuse of `s` var)
|
||||
return s
|
||||
}
|
||||
|
||||
MyOkayFunc(s string, b bool) string {
|
||||
if !b {
|
||||
return s + "hey"
|
||||
}
|
||||
s2 := strings.Replace(s, "blah", "", -1) // doesn't reuse `s` variable
|
||||
return s2
|
||||
}
|
||||
|
||||
MyGreatFunc(s string, b bool) string {
|
||||
if !b {
|
||||
return s + "hey"
|
||||
}
|
||||
return strings.Replace(s, "blah", "", -1) // even cleaner
|
||||
}
|
||||
```
|
||||
|
||||
### Constants in code
|
||||
|
||||
If a function takes a specifier (often a bool) it's sometimes better to name
|
||||
that variable (often with a `const`) rather than leaving a naked `bool` in the
|
||||
code. For example, `x := MyFoo("blah", false)` is less clear than
|
||||
`const useMagic = false; x := MyFoo("blah", useMagic)`.
|
||||
|
||||
### Empty slice declarations
|
||||
|
||||
When declaring a new empty slice, there are three different mechanisms:
|
||||
|
||||
1. `a := []string{}`
|
||||
|
||||
2. `var a []string`
|
||||
|
||||
3. `a := make([]string, 0)`
|
||||
|
||||
In general, we prefer the first method because we find that it is succinct, and
|
||||
very readable. The third method is the least recommended because you're adding
|
||||
extra data that a smart compiler could probably figure out on its own. There are
|
||||
performance implications between these three methods, so unless your code is in
|
||||
a fast path or memory constrained environment where this matters (and that you
|
||||
ideally have proof of this) please use the methods as ordered as much as
|
||||
possible.
|
||||
|
||||
### Consistent ordering
|
||||
|
||||
In general we try to preserve a logical ordering in source files which usually
|
||||
matches the common order of execution that a _lazy evaluator_ would follow.
|
||||
|
||||
This is also the order which is recommended when creating interface types. When
|
||||
implementing an interface, arrange your methods in the same order that they are
|
||||
declared in the interface.
|
||||
|
||||
When implementing code for the various types in the language, please follow this
|
||||
order: `bool`, `str`, `int`, `float`, `list`, `map`, `struct`, `func`.
|
||||
|
||||
For other aspects where you have a set of items, try to be internally consistent
|
||||
as well. For example, if you have two switch statements with `A`, `B`, and `C`,
|
||||
please use the same ordering for these elements elsewhere that they appear in
|
||||
the code and in the commentary if it is not illogical to do so.
|
||||
|
||||
### Product identifiers
|
||||
|
||||
Try to avoid references in the code to `mgmt` or a specific program name string
|
||||
if possible. This makes it easier to rename code if we ever pick a better name
|
||||
or support `libmgmt` better if we embed it. You can use the `Program` variable
|
||||
which is available in numerous places if you want a string to put in the logs.
|
||||
|
||||
It is also recommended to avoid the `go` (programming language name) string if
|
||||
possible. Try to use `golang` if required, since the word `go` is already
|
||||
overloaded, and in particular it was even already used by the
|
||||
[`go!`](https://en.wikipedia.org/wiki/Go!_(programming_language)).
|
||||
|
||||
## Overview for mcl code
|
||||
|
||||
The `mcl` language is quite new, so this guide will probably change over time as
|
||||
we find what's best, and hopefully we'll be able to add an `mclfmt` tool in the
|
||||
future so that less of this needs to be documented. (Patches welcome!)
|
||||
|
||||
### Indentation
|
||||
|
||||
Code indentation is done with tabs. The tab-width is a private preference, which
|
||||
is the beauty of using tabs: you can have your own personal preference. The
|
||||
inventor of `mgmt` uses and recommends a width of eight, and that is what should
|
||||
be used if your tool requires a modeline to be publicly committed.
|
||||
|
||||
### Line length
|
||||
|
||||
We recommend you stick to 80 char line width. If you find yourself with deeper
|
||||
nesting, it might be a hint that your code could be refactored in a more
|
||||
pleasant way.
|
||||
|
||||
### Capitalization
|
||||
|
||||
At the moment, variables, function names, and classes are all lowercase and do
|
||||
not contain underscores. We will probably figure out what style to recommend
|
||||
when the language is a bit further along. For example, we haven't decided if we
|
||||
should have a notion of public and private variables, and if we'd like to
|
||||
reserve capitalization for this situation.
|
||||
|
||||
### Module naming
|
||||
|
||||
We recommend you name your modules with an `mgmt-` prefix. For example, a module
|
||||
about bananas might be named `mgmt-banana`. This is helpful for the useful magic
|
||||
built-in to the module import code, which will by default take a remote import
|
||||
like: `import "https://github.com/purpleidea/mgmt-banana/"` and namespace it as
|
||||
`banana`. Of course you can always pick the namespace yourself on import with:
|
||||
`import "https://github.com/purpleidea/mgmt-banana/" as tomato` or something
|
||||
similar.
|
||||
|
||||
### Imports
|
||||
|
||||
When importing "golang" modules such as "golang/strings" it's recommended to use
|
||||
the `import "golang/strings" as golang_strings` format. This is to avoid
|
||||
confusion with the normal core package you get from `import "strings"`.
|
||||
|
||||
In the long-term, we expect to remove the `"golang/"` namespace when our own
|
||||
standard library is complete enough.
|
||||
|
||||
### Licensing
|
||||
|
||||
We believe that sharing code helps reduce unnecessary re-invention, so that we
|
||||
can [stand on the shoulders of giants](https://en.wikipedia.org/wiki/Standing_on_the_shoulders_of_giants)
|
||||
and hopefully make faster progress in science, medicine, exploration, etc... As
|
||||
a result, we recommend releasing your modules under the [LGPLv3+](https://www.gnu.org/licenses/lgpl-3.0.en.html)
|
||||
license for the maximum balance of freedom and re-usability. We strongly oppose
|
||||
any [CLA](https://en.wikipedia.org/wiki/Contributor_License_Agreement)
|
||||
requirements and believe that the ["inbound==outbound"](https://ref.fedorapeople.org/fontana-linuxcon.html#slide2)
|
||||
rule applies. Lastly, we do not support software patents and we hope you don't
|
||||
either!
|
||||
|
||||
## Suggestions
|
||||
|
||||
If you have any ideas for suggestions or other improvements to this guide,
|
||||
please let us know!
|
||||
103
docs/util/metadata.go
Normal file
103
docs/util/metadata.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
// Package util handles metadata for documentation generation.
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
registeredResourceMetadata = make(map[string]*Metadata) // must initialize
|
||||
registeredFunctionMetadata = make(map[string]*Metadata) // must initialize
|
||||
)
|
||||
|
||||
// RegisterResource records the metadata for a resource of this kind.
|
||||
func RegisterResource(kind string, metadata *Metadata) error {
|
||||
if _, exists := registeredResourceMetadata[kind]; exists {
|
||||
return fmt.Errorf("metadata kind %s is already registered", kind)
|
||||
}
|
||||
|
||||
registeredResourceMetadata[kind] = metadata
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LookupResource looks up the metadata for a resource of this kind.
|
||||
func LookupResource(kind string) (*Metadata, error) {
|
||||
metadata, exists := registeredResourceMetadata[kind]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
// RegisterFunction records the metadata for a function of this name.
|
||||
func RegisterFunction(name string, metadata *Metadata) error {
|
||||
if _, exists := registeredFunctionMetadata[name]; exists {
|
||||
return fmt.Errorf("metadata named %s is already registered", name)
|
||||
}
|
||||
|
||||
registeredFunctionMetadata[name] = metadata
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LookupFunction looks up the metadata for a function of this name.
|
||||
func LookupFunction(name string) (*Metadata, error) {
|
||||
metadata, exists := registeredFunctionMetadata[name]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
// Metadata stores some additional information about the function or resource.
|
||||
// This is used to automatically generate documentation.
|
||||
type Metadata struct {
|
||||
// Filename is the filename (without any base dir path) that this is in.
|
||||
Filename string
|
||||
|
||||
// Typename is the string name of the main resource struct or function.
|
||||
Typename string
|
||||
}
|
||||
|
||||
// GetMetadata returns some metadata about the func. It can be called at any
|
||||
// time. This must not be named the same as the struct it's on or using it as an
|
||||
// anonymous embedded struct will stop us from being able to call this method.
|
||||
func (obj *Metadata) GetMetadata() *Metadata {
|
||||
//if obj == nil { // TODO: Do I need this?
|
||||
// return nil
|
||||
//}
|
||||
return &Metadata{
|
||||
Filename: obj.Filename,
|
||||
Typename: obj.Typename,
|
||||
}
|
||||
}
|
||||
138
engine/autoedge.go
Normal file
138
engine/autoedge.go
Normal file
@@ -0,0 +1,138 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// EdgeableRes is the interface a resource must implement to support automatic
|
||||
// edges. Both the vertices involved in an edge need to implement this for it to
|
||||
// be able to work.
|
||||
type EdgeableRes interface {
|
||||
Res // implement everything in Res but add the additional requirements
|
||||
|
||||
// AutoEdgeMeta lets you get or set meta params for the automatic edges
|
||||
// trait.
|
||||
AutoEdgeMeta() *AutoEdgeMeta
|
||||
|
||||
// SetAutoEdgeMeta lets you set all of the meta params for the automatic
|
||||
// edges trait in a single call.
|
||||
SetAutoEdgeMeta(*AutoEdgeMeta)
|
||||
|
||||
// UIDs includes all params to make a unique identification of this
|
||||
// object.
|
||||
UIDs() []ResUID // most resources only return one
|
||||
|
||||
// AutoEdges returns a struct that implements the AutoEdge interface.
|
||||
// This interface can be used to generate automatic edges to other
|
||||
// resources.
|
||||
AutoEdges() (AutoEdge, error)
|
||||
}
|
||||
|
||||
// AutoEdgeMeta provides some parameters specific to automatic edges.
|
||||
// TODO: currently this only supports disabling the feature per-resource, but in
|
||||
// the future you could conceivably have some small pattern to control it better
|
||||
type AutoEdgeMeta struct {
|
||||
// Disabled specifies that automatic edges should be disabled for this
|
||||
// resource.
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// Cmp compares two AutoEdgeMeta structs and determines if they're equivalent.
|
||||
func (obj *AutoEdgeMeta) Cmp(aem *AutoEdgeMeta) error {
|
||||
if obj.Disabled != aem.Disabled {
|
||||
return fmt.Errorf("values for Disabled are different")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The AutoEdge interface is used to implement the autoedges feature.
|
||||
type AutoEdge interface {
|
||||
Next() []ResUID // call to get list of edges to add
|
||||
Test([]bool) bool // call until false
|
||||
}
|
||||
|
||||
// ResUID is a unique identifier for a resource, namely it's name, and the kind
|
||||
// ("type").
|
||||
type ResUID interface {
|
||||
fmt.Stringer // String() string
|
||||
|
||||
GetName() string
|
||||
GetKind() string
|
||||
|
||||
IFF(ResUID) bool
|
||||
|
||||
IsReversed() bool // true means this resource happens before the generator
|
||||
}
|
||||
|
||||
// The BaseUID struct is used to provide a unique resource identifier.
|
||||
type BaseUID struct {
|
||||
Name string // name and kind are the values of where this is coming from
|
||||
Kind string
|
||||
|
||||
Reversed *bool // piggyback edge information here
|
||||
}
|
||||
|
||||
// GetName returns the name of the resource UID.
|
||||
func (obj *BaseUID) GetName() string {
|
||||
return obj.Name
|
||||
}
|
||||
|
||||
// GetKind returns the kind of the resource UID.
|
||||
func (obj *BaseUID) GetKind() string {
|
||||
return obj.Kind
|
||||
}
|
||||
|
||||
// String returns the canonical string representation for a resource UID.
|
||||
func (obj *BaseUID) String() string {
|
||||
return fmt.Sprintf("%s[%s]", obj.GetKind(), obj.GetName())
|
||||
}
|
||||
|
||||
// IFF looks at two UID's and if and only if they are equivalent, returns true.
|
||||
// If they are not equivalent, it returns false. Most resources will want to
|
||||
// override this method, since it does the important work of actually discerning
|
||||
// if two resources are identical in function.
|
||||
func (obj *BaseUID) IFF(uid ResUID) bool {
|
||||
res, ok := uid.(*BaseUID)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return obj.Name == res.Name
|
||||
}
|
||||
|
||||
// IsReversed is part of the ResUID interface, and true means this resource
|
||||
// happens before the generator.
|
||||
func (obj *BaseUID) IsReversed() bool {
|
||||
if obj.Reversed == nil {
|
||||
panic("programming error!")
|
||||
}
|
||||
return *obj.Reversed
|
||||
}
|
||||
50
engine/autoedge_test.go
Normal file
50
engine/autoedge_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
//go:build !root
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIFF1(t *testing.T) {
|
||||
uid := &BaseUID{Name: "/tmp/unit-test"}
|
||||
same := &BaseUID{Name: "/tmp/unit-test"}
|
||||
diff := &BaseUID{Name: "/tmp/other-file"}
|
||||
|
||||
if !uid.IFF(same) {
|
||||
t.Errorf("basic resource UIDs with the same name should satisfy each other's IFF condition")
|
||||
}
|
||||
|
||||
if uid.IFF(diff) {
|
||||
t.Errorf("basic resource UIDs with different names should NOT satisfy each other's IFF condition")
|
||||
}
|
||||
}
|
||||
108
engine/autogroup.go
Normal file
108
engine/autogroup.go
Normal file
@@ -0,0 +1,108 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/purpleidea/mgmt/pgraph"
|
||||
)
|
||||
|
||||
// GroupableRes is the interface a resource must implement to support automatic
|
||||
// grouping. Default implementations for most of the methods declared in this
|
||||
// interface can be obtained for your resource by anonymously adding the
|
||||
// traits.Groupable struct to your resource implementation.
|
||||
type GroupableRes interface {
|
||||
Res // implement everything in Res but add the additional requirements
|
||||
|
||||
// AutoGroupMeta lets you get or set meta params for the automatic
|
||||
// grouping trait.
|
||||
AutoGroupMeta() *AutoGroupMeta
|
||||
|
||||
// SetAutoGroupMeta lets you set all of the meta params for the
|
||||
// automatic grouping trait in a single call.
|
||||
SetAutoGroupMeta(*AutoGroupMeta)
|
||||
|
||||
// GroupCmp compares two resources and decides if they're suitable for
|
||||
// grouping. This usually needs to be unique to your resource.
|
||||
GroupCmp(res GroupableRes) error
|
||||
|
||||
// GroupRes groups resource argument (res) into self. Callers of this
|
||||
// method should probably also run SetParent.
|
||||
GroupRes(res GroupableRes) error
|
||||
|
||||
// IsGrouped determines if we are grouped.
|
||||
IsGrouped() bool // am I grouped?
|
||||
|
||||
// SetGrouped sets a flag to tell if we are grouped.
|
||||
SetGrouped(bool)
|
||||
|
||||
// GetGroup returns everyone grouped inside me.
|
||||
GetGroup() []GroupableRes // return everyone grouped inside me
|
||||
|
||||
// SetGroup sets the grouped resources into me. Callers of this method
|
||||
// should probably also run SetParent.
|
||||
SetGroup([]GroupableRes)
|
||||
|
||||
// Parent returns the parent groupable resource that I am inside of.
|
||||
Parent() GroupableRes
|
||||
|
||||
// SetParent tells a particular grouped resource who their parent is.
|
||||
SetParent(res GroupableRes)
|
||||
}
|
||||
|
||||
// AutoGroupMeta provides some parameters specific to automatic grouping.
|
||||
// TODO: currently this only supports disabling the feature per-resource, but in
|
||||
// the future you could conceivably have some small pattern to control it better
|
||||
type AutoGroupMeta struct {
|
||||
// Disabled specifies that automatic grouping should be disabled for
|
||||
// this resource.
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// Cmp compares two AutoGroupMeta structs and determines if they're equivalent.
|
||||
func (obj *AutoGroupMeta) Cmp(agm *AutoGroupMeta) error {
|
||||
if obj.Disabled != agm.Disabled {
|
||||
return fmt.Errorf("values for Disabled are different")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AutoGrouper is the required interface to implement an autogrouping algorithm.
|
||||
type AutoGrouper interface {
|
||||
// listed in the order these are typically called in...
|
||||
Name() string // friendly identifier
|
||||
Init(*pgraph.Graph) error // only call once
|
||||
VertexNext() (pgraph.Vertex, pgraph.Vertex, error) // mostly algorithmic
|
||||
VertexCmp(pgraph.Vertex, pgraph.Vertex) error // can we merge these ?
|
||||
VertexMerge(pgraph.Vertex, pgraph.Vertex) (pgraph.Vertex, error) // vertex merge fn to use
|
||||
EdgeMerge(pgraph.Edge, pgraph.Edge) pgraph.Edge // edge merge fn to use
|
||||
VertexTest(bool) (bool, error) // call until false
|
||||
}
|
||||
449
engine/cmp.go
Normal file
449
engine/cmp.go
Normal file
@@ -0,0 +1,449 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/purpleidea/mgmt/pgraph"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
)
|
||||
|
||||
// ResCmp compares two resources by checking multiple aspects. This is the main
|
||||
// entry point for running all the compare steps on two resources. This code is
|
||||
// very similar to AdaptCmp.
|
||||
func ResCmp(r1, r2 Res) error {
|
||||
if r1.Kind() != r2.Kind() {
|
||||
return fmt.Errorf("kind differs")
|
||||
}
|
||||
if r1.Name() != r2.Name() {
|
||||
return fmt.Errorf("name differs")
|
||||
}
|
||||
|
||||
if err := r1.Cmp(r2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: do we need to compare other traits/metaparams?
|
||||
|
||||
m1 := r1.MetaParams()
|
||||
m2 := r2.MetaParams()
|
||||
if (m1 == nil) != (m2 == nil) { // xor
|
||||
return fmt.Errorf("meta params differ")
|
||||
}
|
||||
if m1 != nil && m2 != nil {
|
||||
if err := m1.Cmp(m2); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r1x, ok1 := r1.(RefreshableRes)
|
||||
r2x, ok2 := r2.(RefreshableRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("refreshable differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
if r1x.Refresh() != r2x.Refresh() {
|
||||
return fmt.Errorf("refresh differs")
|
||||
}
|
||||
}
|
||||
|
||||
// compare meta params for resources with auto edges
|
||||
r1e, ok1 := r1.(EdgeableRes)
|
||||
r2e, ok2 := r2.(EdgeableRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("edgeable differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
if r1e.AutoEdgeMeta().Cmp(r2e.AutoEdgeMeta()) != nil {
|
||||
return fmt.Errorf("autoedge differs")
|
||||
}
|
||||
}
|
||||
|
||||
// compare meta params for resources with auto grouping
|
||||
r1g, ok1 := r1.(GroupableRes)
|
||||
r2g, ok2 := r2.(GroupableRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("groupable differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
if r1g.AutoGroupMeta().Cmp(r2g.AutoGroupMeta()) != nil {
|
||||
return fmt.Errorf("autogroup differs")
|
||||
}
|
||||
|
||||
// if resources are grouped, are the groups the same?
|
||||
if i, j := r1g.GetGroup(), r2g.GetGroup(); len(i) != len(j) {
|
||||
return fmt.Errorf("autogroup groups differ")
|
||||
} else if len(i) > 0 { // trick the golinter
|
||||
|
||||
// Sort works with Res, so convert the lists to that
|
||||
iRes := []Res{}
|
||||
for _, r := range i {
|
||||
res := r.(Res)
|
||||
iRes = append(iRes, res)
|
||||
}
|
||||
jRes := []Res{}
|
||||
for _, r := range j {
|
||||
res := r.(Res)
|
||||
jRes = append(jRes, res)
|
||||
}
|
||||
|
||||
ix, jx := Sort(iRes), Sort(jRes) // now sort :)
|
||||
for k := range ix {
|
||||
// compare sub resources
|
||||
if err := ResCmp(ix[k], jx[k]); err != nil {
|
||||
//fmt.Printf("bad Cmp: %+v <> %+v for: %+v <> %+v err: %+v\n", r1, r2, ix[k], jx[k], err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r1r, ok1 := r1.(RecvableRes)
|
||||
r2r, ok2 := r2.(RecvableRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("recvable differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
v1 := r1r.Recv()
|
||||
v2 := r2r.Recv()
|
||||
|
||||
// XXX: Our Send/Recv in the lib/main.go doesn't seem to be
|
||||
// pulling this in, so this always compares differently. We can
|
||||
// comment it out for now, since it's not too consequential.
|
||||
// XXX: Find out what the issue is and fix it for here and send.
|
||||
// XXX: The below errors are commented out until this is fixed.
|
||||
if (v1 == nil) != (v2 == nil) { // xor
|
||||
//return fmt.Errorf("recv params differ")
|
||||
}
|
||||
if v1 != nil && v2 != nil {
|
||||
if len(v1) != len(v2) {
|
||||
//return fmt.Errorf("recv param lengths differ")
|
||||
}
|
||||
for key, send1 := range v1 { // map[string]*engine.Send
|
||||
send2, exists := v2[key]
|
||||
if !exists {
|
||||
//return fmt.Errorf("recv param key %s doesn't exist", key)
|
||||
}
|
||||
if (send1 == nil) != (send2 == nil) { // xor
|
||||
//return fmt.Errorf("recv param key %s send differs", key)
|
||||
}
|
||||
if send1 != nil && send2 != nil && send1.Key != send2.Key {
|
||||
//return fmt.Errorf("recv param key %s send key differs (%v != %v)", key, send1.Key, send2.Key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r1s, ok1 := r1.(SendableRes)
|
||||
r2s, ok2 := r2.(SendableRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("sendable differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
s1 := r1s.Sent()
|
||||
s2 := r2s.Sent()
|
||||
|
||||
// XXX: Our Send/Recv in the lib/main.go doesn't seem to be
|
||||
// pulling this in, so this always compares differently. We can
|
||||
// comment it out for now, since it's not too consequential.
|
||||
// XXX: Find out what the issue is and fix it for here and recv.
|
||||
// XXX: The below errors are commented out until this is fixed.
|
||||
if (s1 == nil) != (s2 == nil) { // xor
|
||||
//return fmt.Errorf("send params differ")
|
||||
}
|
||||
if s1 != nil && s2 != nil {
|
||||
// TODO: reflect.DeepEqual?
|
||||
//return fmt.Errorf("send params exist")
|
||||
}
|
||||
}
|
||||
|
||||
// compare meta params for resources with reversible traits
|
||||
r1v, ok1 := r1.(ReversibleRes)
|
||||
r2v, ok2 := r2.(ReversibleRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("reversible differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
if r1v.ReversibleMeta().Cmp(r2v.ReversibleMeta()) != nil {
|
||||
return fmt.Errorf("reversible differs")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AdaptCmp compares two resources by checking multiple aspects. This is the
|
||||
// main entry point for running all the compatible compare steps on two
|
||||
// resources. This code is very similar to ResCmp.
|
||||
func AdaptCmp(r1, r2 CompatibleRes) error {
|
||||
if r1.Kind() != r2.Kind() {
|
||||
return fmt.Errorf("kind differs")
|
||||
}
|
||||
if r1.Name() != r2.Name() {
|
||||
return fmt.Errorf("name differs")
|
||||
}
|
||||
|
||||
// run `Adapts` instead of `Cmp`
|
||||
if err := r1.Adapts(r2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: do we need to compare other traits/metaparams?
|
||||
|
||||
m1 := r1.MetaParams()
|
||||
m2 := r2.MetaParams()
|
||||
if (m1 == nil) != (m2 == nil) { // xor
|
||||
return fmt.Errorf("meta params differ")
|
||||
}
|
||||
if m1 != nil && m2 != nil {
|
||||
if err := m1.Cmp(m2); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// we don't need to compare refresh, since those can always be merged...
|
||||
|
||||
// compare meta params for resources with auto edges
|
||||
r1e, ok1 := r1.(EdgeableRes)
|
||||
r2e, ok2 := r2.(EdgeableRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("edgeable differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
if r1e.AutoEdgeMeta().Cmp(r2e.AutoEdgeMeta()) != nil {
|
||||
return fmt.Errorf("autoedge differs")
|
||||
}
|
||||
}
|
||||
|
||||
// compare meta params for resources with auto grouping
|
||||
r1g, ok1 := r1.(GroupableRes)
|
||||
r2g, ok2 := r2.(GroupableRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("groupable differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
if r1g.AutoGroupMeta().Cmp(r2g.AutoGroupMeta()) != nil {
|
||||
return fmt.Errorf("autogroup differs")
|
||||
}
|
||||
|
||||
// if resources are grouped, are the groups the same?
|
||||
if i, j := r1g.GetGroup(), r2g.GetGroup(); len(i) != len(j) {
|
||||
return fmt.Errorf("autogroup groups differ")
|
||||
} else if len(i) > 0 { // trick the golinter
|
||||
|
||||
// Sort works with Res, so convert the lists to that
|
||||
iRes := []Res{}
|
||||
for _, r := range i {
|
||||
res := r.(Res)
|
||||
iRes = append(iRes, res)
|
||||
}
|
||||
jRes := []Res{}
|
||||
for _, r := range j {
|
||||
res := r.(Res)
|
||||
jRes = append(jRes, res)
|
||||
}
|
||||
|
||||
ix, jx := Sort(iRes), Sort(jRes) // now sort :)
|
||||
for k := range ix {
|
||||
// compare sub resources
|
||||
// TODO: should we use AdaptCmp here?
|
||||
// TODO: how would they run `Merge` ? (we don't)
|
||||
// this code path will probably not run, because
|
||||
// it is called in the lang before autogrouping!
|
||||
if err := ResCmp(ix[k], jx[k]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r1r, ok1 := r1.(RecvableRes)
|
||||
r2r, ok2 := r2.(RecvableRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("recvable differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
v1 := r1r.Recv()
|
||||
v2 := r2r.Recv()
|
||||
|
||||
// XXX: Our Send/Recv in the lib/main.go doesn't seem to be
|
||||
// pulling this in, so this always compares differently. We can
|
||||
// comment it out for now, since it's not too consequential.
|
||||
// XXX: Find out what the issue is and fix it for here and send.
|
||||
// XXX: The below errors are commented out until this is fixed.
|
||||
if (v1 == nil) != (v2 == nil) { // xor
|
||||
//return fmt.Errorf("recv params differ")
|
||||
}
|
||||
if v1 != nil && v2 != nil {
|
||||
if len(v1) != len(v2) {
|
||||
//return fmt.Errorf("recv param lengths differ")
|
||||
}
|
||||
for key, send1 := range v1 { // map[string]*engine.Send
|
||||
send2, exists := v2[key]
|
||||
if !exists {
|
||||
//return fmt.Errorf("recv param key %s doesn't exist", key)
|
||||
}
|
||||
if (send1 == nil) != (send2 == nil) { // xor
|
||||
//return fmt.Errorf("recv param key %s send differs", key)
|
||||
}
|
||||
if send1 != nil && send2 != nil && send1.Key != send2.Key {
|
||||
//return fmt.Errorf("recv param key %s send key differs (%v != %v)", key, send1.Key, send2.Key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r1s, ok1 := r1.(SendableRes)
|
||||
r2s, ok2 := r2.(SendableRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("sendable differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
s1 := r1s.Sent()
|
||||
s2 := r2s.Sent()
|
||||
|
||||
// XXX: Our Send/Recv in the lib/main.go doesn't seem to be
|
||||
// pulling this in, so this always compares differently. We can
|
||||
// comment it out for now, since it's not too consequential.
|
||||
// XXX: Find out what the issue is and fix it for here and recv.
|
||||
// XXX: The below errors are commented out until this is fixed.
|
||||
if (s1 == nil) != (s2 == nil) { // xor
|
||||
//return fmt.Errorf("send params differ")
|
||||
}
|
||||
if s1 != nil && s2 != nil {
|
||||
// TODO: reflect.DeepEqual?
|
||||
//return fmt.Errorf("send params exist")
|
||||
}
|
||||
}
|
||||
|
||||
// compare meta params for resources with reversible traits
|
||||
r1v, ok1 := r1.(ReversibleRes)
|
||||
r2v, ok2 := r2.(ReversibleRes)
|
||||
if ok1 != ok2 {
|
||||
return fmt.Errorf("reversible differs") // they must be different (optional)
|
||||
}
|
||||
if ok1 && ok2 {
|
||||
if r1v.ReversibleMeta().Cmp(r2v.ReversibleMeta()) != nil {
|
||||
return fmt.Errorf("reversible differs")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VertexCmpFn returns if two vertices are equivalent. It errors if they can't
|
||||
// be compared because one is not a vertex. This returns true if equal.
|
||||
// TODO: shouldn't the first argument be an `error` instead?
|
||||
func VertexCmpFn(v1, v2 pgraph.Vertex) (bool, error) {
|
||||
r1, ok := v1.(Res)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("v1 is not a Res")
|
||||
}
|
||||
r2, ok := v2.(Res)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("v2 is not a Res")
|
||||
}
|
||||
|
||||
if err := ResCmp(r1, r2); err != nil {
|
||||
//fmt.Printf("bad Cmp: %p %+v <> %p %+v err: %+v\n", r1, r1, r2, r2, err)
|
||||
return false, nil
|
||||
}
|
||||
//fmt.Printf("ok Cmp: %p %+v <> %p %+v\n", r1, r1, r2, r2)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// EdgeCmpFn returns if two edges are equivalent. It errors if they can't be
|
||||
// compared because one is not an edge. This returns true if equal.
|
||||
// TODO: shouldn't the first argument be an `error` instead?
|
||||
func EdgeCmpFn(e1, e2 pgraph.Edge) (bool, error) {
|
||||
edge1, ok := e1.(*Edge)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("e1 is not an Edge")
|
||||
}
|
||||
edge2, ok := e2.(*Edge)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("e2 is not an Edge")
|
||||
}
|
||||
return edge1.Cmp(edge2) == nil, nil
|
||||
}
|
||||
|
||||
// ResGraphMapper compares two graphs, and gives us a mapping from new to old
|
||||
// based on the resource kind and name only. This allows us to know which
|
||||
// previous resource might have data to pass on to the new version in the next
|
||||
// generation.
|
||||
// FIXME: Optimize this for performance since it runs a lot...
|
||||
func ResGraphMapper(oldGraph, newGraph *pgraph.Graph) (map[RecvableRes]RecvableRes, error) {
|
||||
mapper := make(map[RecvableRes]RecvableRes) // new -> old based on name and kind only?
|
||||
cmp := func(r1, r2 Res) error {
|
||||
if r1.Kind() != r2.Kind() {
|
||||
return fmt.Errorf("kind differs")
|
||||
}
|
||||
if r1.Name() != r2.Name() {
|
||||
return fmt.Errorf("name differs")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// XXX: run this as a topological sort or reverse topological sort?
|
||||
for v := range newGraph.Adjacency() { // loop through the vertices (resources)
|
||||
r, ok := v.(RecvableRes)
|
||||
if !ok {
|
||||
continue // skip
|
||||
}
|
||||
fn := func(vv pgraph.Vertex) (bool, error) {
|
||||
rr, ok := vv.(Res)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("not a Res")
|
||||
}
|
||||
|
||||
if err := cmp(rr, r); err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
vertex, err := oldGraph.VertexMatchFn(fn)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf(err, "VertexMatchFn failed")
|
||||
}
|
||||
if vertex == nil {
|
||||
continue // skip (error?)
|
||||
}
|
||||
res, ok := vertex.(RecvableRes)
|
||||
if !ok {
|
||||
continue // skip (error?)
|
||||
}
|
||||
|
||||
mapper[r] = res
|
||||
}
|
||||
|
||||
return mapper, nil
|
||||
}
|
||||
183
engine/copy.go
Normal file
183
engine/copy.go
Normal file
@@ -0,0 +1,183 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
)
|
||||
|
||||
// ResCopy copies a resource. This is the main entry point for copying a
|
||||
// resource since it does all the common engine-level copying as well.
|
||||
func ResCopy(r CopyableRes) (CopyableRes, error) {
|
||||
res := r.Copy()
|
||||
res.SetKind(r.Kind())
|
||||
res.SetName(r.Name())
|
||||
|
||||
if x, ok := r.(MetaRes); ok {
|
||||
dst, ok := res.(MetaRes)
|
||||
if !ok {
|
||||
// programming error
|
||||
panic("meta interfaces are illogical")
|
||||
}
|
||||
dst.SetMetaParams(x.MetaParams().Copy()) // copy b/c we have it
|
||||
}
|
||||
|
||||
if x, ok := r.(RefreshableRes); ok {
|
||||
dst, ok := res.(RefreshableRes)
|
||||
if !ok {
|
||||
// programming error
|
||||
panic("refresh interfaces are illogical")
|
||||
}
|
||||
dst.SetRefresh(x.Refresh()) // no need to copy atm
|
||||
}
|
||||
|
||||
// copy meta params for resources with auto edges
|
||||
if x, ok := r.(EdgeableRes); ok {
|
||||
dst, ok := res.(EdgeableRes)
|
||||
if !ok {
|
||||
// programming error
|
||||
panic("autoedge interfaces are illogical")
|
||||
}
|
||||
dst.SetAutoEdgeMeta(x.AutoEdgeMeta()) // no need to copy atm
|
||||
}
|
||||
|
||||
// copy meta params for resources with auto grouping
|
||||
if x, ok := r.(GroupableRes); ok {
|
||||
dst, ok := res.(GroupableRes)
|
||||
if !ok {
|
||||
// programming error
|
||||
panic("autogroup interfaces are illogical")
|
||||
}
|
||||
dst.SetAutoGroupMeta(x.AutoGroupMeta()) // no need to copy atm
|
||||
|
||||
grouped := []GroupableRes{}
|
||||
for _, g := range x.GetGroup() {
|
||||
g0, ok := g.(CopyableRes)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("resource wasn't copyable")
|
||||
}
|
||||
g1, err := ResCopy(g0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g2, ok := g1.(GroupableRes)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("resource wasn't groupable")
|
||||
}
|
||||
g2.SetParent(dst) // store who my parent is
|
||||
grouped = append(grouped, g2)
|
||||
}
|
||||
dst.SetGroup(grouped)
|
||||
}
|
||||
|
||||
if x, ok := r.(RecvableRes); ok {
|
||||
dst, ok := res.(RecvableRes)
|
||||
if !ok {
|
||||
// programming error
|
||||
panic("recv interfaces are illogical")
|
||||
}
|
||||
dst.SetRecv(x.Recv()) // no need to copy atm
|
||||
}
|
||||
|
||||
if x, ok := r.(SendableRes); ok {
|
||||
dst, ok := res.(SendableRes)
|
||||
if !ok {
|
||||
// programming error
|
||||
panic("send interfaces are illogical")
|
||||
}
|
||||
if err := dst.Send(x.Sent()); err != nil { // no need to copy atm
|
||||
return nil, errwrap.Wrapf(err, "can't copy send")
|
||||
}
|
||||
}
|
||||
|
||||
// copy meta params for resources with reversible traits
|
||||
if x, ok := r.(ReversibleRes); ok {
|
||||
dst, ok := res.(ReversibleRes)
|
||||
if !ok {
|
||||
// programming error
|
||||
panic("reversible interfaces are illogical")
|
||||
}
|
||||
dst.SetReversibleMeta(x.ReversibleMeta()) // no need to copy atm
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ResMerge merges a set of resources that are compatible with each other. This
|
||||
// is the main entry point for the merging. They must each successfully be able
|
||||
// to run AdaptCmp without error.
|
||||
func ResMerge(r ...CompatibleRes) (CompatibleRes, error) {
|
||||
if len(r) == 0 {
|
||||
return nil, fmt.Errorf("zero resources given")
|
||||
}
|
||||
if len(r) == 1 {
|
||||
return r[0], nil
|
||||
}
|
||||
if len(r) > 2 {
|
||||
r0 := r[0]
|
||||
r1, err := ResMerge(r[1:]...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ResMerge(r0, r1)
|
||||
}
|
||||
// now we have r[0] and r[1] to merge here...
|
||||
r0 := r[0]
|
||||
r1 := r[1]
|
||||
if err := AdaptCmp(r0, r1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := r0.Merge(r1) // resource method of this interface
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// meta should have come over in the copy
|
||||
|
||||
if x, ok := res.(RefreshableRes); ok {
|
||||
x0, ok0 := r0.(RefreshableRes)
|
||||
x1, ok1 := r1.(RefreshableRes)
|
||||
if !ok0 || !ok1 {
|
||||
// programming error
|
||||
panic("refresh interfaces are illogical")
|
||||
}
|
||||
|
||||
x.SetRefresh(x0.Refresh() || x1.Refresh()) // true if either is!
|
||||
}
|
||||
|
||||
// the other traits and metaparams can't be merged easily... so we don't
|
||||
// merge them, and if they were present and differed, and weren't copied
|
||||
// in the ResCopy method, then we should have errored above in AdaptCmp!
|
||||
|
||||
return res, nil
|
||||
}
|
||||
33
engine/doc.go
Normal file
33
engine/doc.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
// Package engine represents the implementation of the resource engine that runs
|
||||
// the graph of resources in real-time. This package has the common imports that
|
||||
// most consumers use directly.
|
||||
package engine
|
||||
72
engine/edge.go
Normal file
72
engine/edge.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Mgmt
|
||||
// Copyright (C) James Shubin and the project contributors
|
||||
// Written by James Shubin <james@shubin.ca> and the project contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Additional permission under GNU GPL version 3 section 7
|
||||
//
|
||||
// If you modify this program, or any covered work, by linking or combining it
|
||||
// with embedded mcl code and modules (and that the embedded mcl code and
|
||||
// modules which link with this program, contain a copy of their source code in
|
||||
// the authoritative form) containing parts covered by the terms of any other
|
||||
// license, the licensors of this program grant you additional permission to
|
||||
// convey the resulting work. Furthermore, the licensors of this program grant
|
||||
// the original author, James Shubin, additional permission to update this
|
||||
// additional permission if he deems it necessary to achieve the goals of this
|
||||
// additional permission.
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Edge is a struct that represents a graph's edge.
|
||||
type Edge struct {
|
||||
Name string
|
||||
Notify bool // should we send a refresh notification along this edge?
|
||||
|
||||
refresh bool // is there a notify pending for the dest vertex ?
|
||||
}
|
||||
|
||||
// String is a required method of the Edge interface that we must fulfill.
|
||||
func (obj *Edge) String() string {
|
||||
return obj.Name
|
||||
}
|
||||
|
||||
// Cmp compares this edge to another. It returns nil if they are equivalent.
|
||||
func (obj *Edge) Cmp(edge *Edge) error {
|
||||
if obj.Name != edge.Name {
|
||||
return fmt.Errorf("edge names differ")
|
||||
}
|
||||
if obj.Notify != edge.Notify {
|
||||
return fmt.Errorf("notify values differ")
|
||||
}
|
||||
// FIXME: should we compare this as well?
|
||||
//if obj.refresh != edge.refresh {
|
||||
// return fmt.Errorf("refresh values differ")
|
||||
//}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Refresh returns the pending refresh status of this edge.
|
||||
func (obj *Edge) Refresh() bool {
|
||||
return obj.refresh
|
||||
}
|
||||
|
||||
// SetRefresh sets the pending refresh status of this edge.
|
||||
func (obj *Edge) SetRefresh(b bool) {
|
||||
obj.refresh = b
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user