Compare commits
432 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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
|
||||
|
||||
@@ -23,3 +23,6 @@ indent_style = tab
|
||||
|
||||
[*.mcl]
|
||||
indent_style = tab
|
||||
|
||||
[*.txtar]
|
||||
indent_style = tab
|
||||
|
||||
16
.github/workflows/test.yaml
vendored
16
.github/workflows/test.yaml
vendored
@@ -27,29 +27,27 @@ jobs:
|
||||
# macos tests are currently failing in CI
|
||||
#- macos-latest
|
||||
golang_version:
|
||||
# TODO: add 1.15.x and tip
|
||||
# TODO: add 1.21.x and tip
|
||||
# minimum required and latest published go_version
|
||||
#- 1.13
|
||||
- 1.15
|
||||
- "1.20"
|
||||
test_block:
|
||||
- basic
|
||||
- shell
|
||||
- race
|
||||
#fail-fast: false
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
# Do not shallow fetch, will fail when building bindata/
|
||||
# The path can't be absolute, so we need to move it to the
|
||||
# expected location later.
|
||||
# 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@v2
|
||||
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@v2
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.golang_version }}
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,7 +7,6 @@ old/
|
||||
tmp/
|
||||
*WIP
|
||||
*_stringer.go
|
||||
bindata/*.go
|
||||
mgmt
|
||||
mgmt.static
|
||||
# crossbuild artifacts
|
||||
|
||||
39
.gitmodules
vendored
39
.gitmodules
vendored
@@ -1,39 +0,0 @@
|
||||
[submodule "vendor/github.com/coreos/etcd"]
|
||||
path = vendor/go.etcd.io/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
|
||||
[submodule "vendor/honnef.co/go/augeas"]
|
||||
path = vendor/honnef.co/go/augeas
|
||||
url = https://github.com/dominikh/go-augeas/
|
||||
[submodule "vendor/github.com/grpc-ecosystem/go-grpc-prometheus"]
|
||||
path = vendor/github.com/grpc-ecosystem/go-grpc-prometheus
|
||||
url = https://github.com/grpc-ecosystem/go-grpc-prometheus
|
||||
[submodule "vendor/github.com/ugorji/go"]
|
||||
path = vendor/github.com/ugorji/go
|
||||
url = https://github.com/ugorji/go
|
||||
[submodule "vendor/github.com/purpleidea/docker"]
|
||||
path = vendor/github.com/docker/docker
|
||||
url = https://github.com/purpleidea/docker
|
||||
[submodule "vendor/github.com/purpleidea/distribution"]
|
||||
path = vendor/github.com/docker/distribution
|
||||
url = https://github.com/purpleidea/distribution
|
||||
[submodule "vendor/github.com/hashicorp/go-multierror"]
|
||||
path = vendor/github.com/hashicorp/go-multierror
|
||||
url = https://github.com/hashicorp/go-multierror
|
||||
[submodule "vendor/github.com/containerd/containerd"]
|
||||
path = vendor/github.com/containerd/containerd
|
||||
url = https://github.com/purpleidea/containerd
|
||||
[submodule "vendor/github.com/hashicorp/consul"]
|
||||
path = vendor/github.com/hashicorp/consul
|
||||
url = https://github.com/hashicorp/consul/
|
||||
10
.travis.yml
10
.travis.yml
@@ -24,21 +24,21 @@ install: 'make deps'
|
||||
matrix:
|
||||
fast_finish: false
|
||||
allow_failures:
|
||||
- go: 1.14.x
|
||||
- go: 1.21.x
|
||||
- go: tip
|
||||
- os: osx
|
||||
# include only one build for osx for a quicker build as the nr. of these runners are sparse
|
||||
include:
|
||||
- name: "basic tests"
|
||||
go: 1.13.x
|
||||
go: 1.20.x
|
||||
env: TEST_BLOCK=basic
|
||||
- name: "shell tests"
|
||||
go: 1.13.x
|
||||
go: 1.20.x
|
||||
env: TEST_BLOCK=shell
|
||||
- name: "race tests"
|
||||
go: 1.13.x
|
||||
go: 1.20.x
|
||||
env: TEST_BLOCK=race
|
||||
- go: 1.14.x
|
||||
- go: 1.21.x
|
||||
- go: tip
|
||||
- os: osx
|
||||
script: 'TEST_BLOCK="$TEST_BLOCK" make test'
|
||||
|
||||
1
AUTHORS
1
AUTHORS
@@ -11,3 +11,4 @@ Johan Bloemberg
|
||||
Jonathan Gold
|
||||
Julien Pivotto
|
||||
Paul Morgan
|
||||
Samuel Gélineau
|
||||
|
||||
8
COPYING
8
COPYING
@@ -1,7 +1,7 @@
|
||||
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.
|
||||
|
||||
@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
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.
|
||||
|
||||
@@ -664,11 +664,11 @@ 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 GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
<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
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
<https://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
16
COPYRIGHT
16
COPYRIGHT
@@ -1,5 +1,5 @@
|
||||
Mgmt
|
||||
Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
Copyright (C) 2013-2024+ 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
|
||||
@@ -13,4 +13,16 @@ 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 <http://www.gnu.org/licenses/>.
|
||||
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.
|
||||
|
||||
295
Makefile
295
Makefile
@@ -1,5 +1,5 @@
|
||||
# Mgmt
|
||||
# Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
# Copyright (C) 2013-2024+ 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
|
||||
@@ -13,19 +13,31 @@
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
# 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 lang path deps run race bindata generate build build-debug crossbuild clean test gofmt yamlfmt format docs
|
||||
.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-30 mkosi_fedora-29 mkosi_centos-7 mkosi_debian-10 mkosi_ubuntu-bionic mkosi_archlinux
|
||||
.PHONY: release releases_path release_fedora-30 release_fedora-29 release_centos-7 release_debian-10 release_ubuntu-bionic release_archlinux
|
||||
.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 bindata
|
||||
.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/funcs/ -name '*.mcl' -not -path 'old/*' -not -path 'tmp/*')
|
||||
MCL_FILES := $(shell find lang/ -name '*.mcl' -not -path 'old/*' -not -path 'tmp/*')
|
||||
|
||||
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))
|
||||
@@ -36,6 +48,13 @@ ifeq ($(VERSION),$(SVERSION))
|
||||
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
|
||||
@@ -44,7 +63,7 @@ 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
|
||||
@@ -53,27 +72,69 @@ GOOSARCHES ?= linux/amd64 linux/ppc64 linux/ppc64le linux/arm64 darwin/amd64
|
||||
GOHOSTOS = $(shell go env GOHOSTOS)
|
||||
GOHOSTARCH = $(shell go env GOHOSTARCH)
|
||||
|
||||
TOKEN_FEDORA-30 = fedora-30
|
||||
TOKEN_FEDORA-29 = fedora-29
|
||||
TOKEN_CENTOS-7 = centos-7
|
||||
TOKEN_DEBIAN-10 = debian-10
|
||||
TOKEN_UBUNTU-BIONIC = ubuntu-bionic
|
||||
TOKEN_ARCHLINUX = archlinux
|
||||
# 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_FEDORA-30 = mgmt-$(TOKEN_FEDORA-30)-$(VERSION)-1.x86_64.rpm
|
||||
FILE_FEDORA-29 = mgmt-$(TOKEN_FEDORA-29)-$(VERSION)-1.x86_64.rpm
|
||||
FILE_CENTOS-7 = mgmt-$(TOKEN_CENTOS-7)-$(VERSION)-1.x86_64.rpm
|
||||
FILE_DEBIAN-10 = mgmt_$(TOKEN_DEBIAN-10)_$(VERSION)_amd64.deb
|
||||
FILE_UBUNTU-BIONIC = mgmt_$(TOKEN_UBUNTU-BIONIC)_$(VERSION)_amd64.deb
|
||||
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_FEDORA-30 = releases/$(VERSION)/$(TOKEN_FEDORA-30)/$(FILE_FEDORA-30)
|
||||
PKG_FEDORA-29 = releases/$(VERSION)/$(TOKEN_FEDORA-29)/$(FILE_FEDORA-29)
|
||||
PKG_CENTOS-7 = releases/$(VERSION)/$(TOKEN_CENTOS-7)/$(FILE_CENTOS-7)
|
||||
PKG_DEBIAN-10 = releases/$(VERSION)/$(TOKEN_DEBIAN-10)/$(FILE_DEBIAN-10)
|
||||
PKG_UBUNTU-BIONIC = releases/$(VERSION)/$(TOKEN_UBUNTU-BIONIC)/$(FILE_UBUNTU-BIONIC)
|
||||
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
|
||||
|
||||
@@ -137,11 +198,6 @@ run: ## run mgmt
|
||||
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 files from non-go source
|
||||
bindata: ## generate go files from non-go sources
|
||||
$(MAKE) --quiet -C bindata
|
||||
$(MAKE) --quiet -C lang/funcs
|
||||
|
||||
generate:
|
||||
go generate
|
||||
|
||||
@@ -153,10 +209,10 @@ lang: ## generates the lexer/parser for the language frontend
|
||||
$(PROGRAM): build/mgmt-${GOHOSTOS}-${GOHOSTARCH} ## build an mgmt binary for current host os/arch
|
||||
cp -a $< $@
|
||||
|
||||
$(PROGRAM).static: $(GO_FILES) $(MCL_FILES)
|
||||
$(PROGRAM).static: $(GO_FILES) $(MCL_FILES) go.mod go.sum
|
||||
@echo "Building: $(PROGRAM).static, version: $(SVERSION)..."
|
||||
go generate
|
||||
go build -a -installsuffix cgo -tags netgo -ldflags '-extldflags "-static" -X main.program=$(PROGRAM) -X main.version=$(SVERSION) -s -w' -o $(PROGRAM).static $(BUILD_FLAGS);
|
||||
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);
|
||||
|
||||
build: LDFLAGS=-s -w ## build a fresh mgmt binary
|
||||
build: $(PROGRAM)
|
||||
@@ -168,22 +224,23 @@ build-debug: $(PROGRAM)
|
||||
# extract os and arch from target pattern
|
||||
GOOS=$(firstword $(subst -, ,$*))
|
||||
GOARCH=$(lastword $(subst -, ,$*))
|
||||
build/mgmt-%: $(GO_FILES) $(MCL_FILES) | bindata lang funcgen
|
||||
build/mgmt-%: $(GO_FILES) $(MCL_FILES) go.mod go.sum | lang funcgen
|
||||
@echo "Building: $(PROGRAM), os/arch: $*, version: $(SVERSION)..."
|
||||
@time env GOOS=${GOOS} GOARCH=${GOARCH} go build -i -ldflags=$(PKGNAME)="-X main.program=$(PROGRAM) -X main.version=$(SVERSION) ${LDFLAGS}" -o $@ $(BUILD_FLAGS)
|
||||
time env GOOS=${GOOS} GOARCH=${GOARCH} go build $(TRIMPATH) -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 bindata clean
|
||||
$(MAKE) --quiet -C lang/funcs clean
|
||||
$(MAKE) --quiet -C lang clean
|
||||
$(MAKE) --quiet -C misc/mkosi clean
|
||||
rm -f lang/funcs/core/generated_funcs.go || true
|
||||
rm -f lang/funcs/core/generated_funcs_test.go || true
|
||||
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`
|
||||
@@ -365,25 +422,25 @@ tag: ## tags a new release
|
||||
#
|
||||
# mkosi
|
||||
#
|
||||
mkosi: mkosi_fedora-30 mkosi_fedora-29 mkosi_centos-7 mkosi_debian-10 mkosi_ubuntu-bionic mkosi_archlinux ## builds distro packages via 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-30: releases/$(VERSION)/.mkdir
|
||||
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-29: releases/$(VERSION)/.mkdir
|
||||
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_centos-7: releases/$(VERSION)/.mkdir
|
||||
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-10: releases/$(VERSION)/.mkdir
|
||||
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-bionic: releases/$(VERSION)/.mkdir
|
||||
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)/"`
|
||||
|
||||
@@ -394,31 +451,73 @@ mkosi_archlinux: releases/$(VERSION)/.mkdir
|
||||
#
|
||||
# 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_fedora-30: $(PKG_FEDORA-30)
|
||||
release_fedora-29: $(PKG_FEDORA-29)
|
||||
release_centos-7: $(PKG_CENTOS-7)
|
||||
release_debian-10: $(PKG_DEBIAN-10)
|
||||
release_ubuntu-bionic: $(PKG_UBUNTU-BIONIC)
|
||||
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: $(PKG_FEDORA-30) $(PKG_FEDORA-29) $(PKG_CENTOS-7) $(PKG_DEBIAN-10) $(PKG_UBUNTU-BIONIC) $(PKG_ARCHLINUX) $(SHA256SUMS_ASC)
|
||||
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." ) \
|
||||
-a $(PKG_FEDORA-30) \
|
||||
-a $(PKG_FEDORA-29) \
|
||||
-a $(PKG_CENTOS-7) \
|
||||
-a $(PKG_DEBIAN-10) \
|
||||
-a $(PKG_UBUNTU-BIONIC) \
|
||||
-a $(PKG_ARCHLINUX) \
|
||||
` [ -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 \
|
||||
@@ -426,56 +525,92 @@ releases/$(VERSION)/mgmt-release.url: $(PKG_FEDORA-30) $(PKG_FEDORA-29) $(PKG_CE
|
||||
|| rm -f releases/$(VERSION)/mgmt-release.url
|
||||
|
||||
releases/$(VERSION)/.mkdir:
|
||||
mkdir -p releases/$(VERSION)/{$(TOKEN_FEDORA-30),$(TOKEN_FEDORA-29),$(TOKEN_CENTOS-7),$(TOKEN_DEBIAN-10),$(TOKEN_UBUNTU-BIONIC),$(TOKEN_ARCHLINUX)}/ && touch 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
|
||||
|
||||
releases/$(VERSION)/$(TOKEN_FEDORA-30)/changelog: $(PROGRAM) 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-30): releases/$(VERSION)/$(TOKEN_FEDORA-30)/changelog
|
||||
$(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-30)" libvirt-devel augeas-devel
|
||||
|
||||
releases/$(VERSION)/$(TOKEN_FEDORA-29)/changelog: $(PROGRAM) releases/$(VERSION)/.mkdir
|
||||
@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-29): releases/$(VERSION)/$(TOKEN_FEDORA-29)/changelog
|
||||
$(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-29)" libvirt-devel augeas-devel
|
||||
|
||||
releases/$(VERSION)/$(TOKEN_CENTOS-7)/changelog: $(PROGRAM) releases/$(VERSION)/.mkdir
|
||||
@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_CENTOS-7): releases/$(VERSION)/$(TOKEN_CENTOS-7)/changelog
|
||||
$(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_CENTOS-7)" libvirt-devel augeas-devel
|
||||
|
||||
releases/$(VERSION)/$(TOKEN_DEBIAN-10)/changelog: $(PROGRAM) releases/$(VERSION)/.mkdir
|
||||
@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-10): releases/$(VERSION)/$(TOKEN_DEBIAN-10)/changelog
|
||||
$(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-10)" libvirt-dev libaugeas-dev
|
||||
|
||||
releases/$(VERSION)/$(TOKEN_UBUNTU-BIONIC)/changelog: $(PROGRAM) releases/$(VERSION)/.mkdir
|
||||
@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-BIONIC): releases/$(VERSION)/$(TOKEN_UBUNTU-BIONIC)/changelog
|
||||
$(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-BIONIC)" libvirt-dev libaugeas-dev
|
||||
|
||||
@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): $(PKG_FEDORA-30) $(PKG_FEDORA-29) $(PKG_CENTOS-7) $(PKG_DEBIAN-10) $(PKG_UBUNTU-BIONIC) $(PKG_ARCHLINUX)
|
||||
$(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 $(PKG_FEDORA-30) $(PKG_FEDORA-29) $(PKG_CENTOS-7) $(PKG_DEBIAN-10) $(PKG_UBUNTU-BIONIC) $(PKG_ARCHLINUX) | awk -F '/| ' '{print $$1" "$$6}' > $(SHA256SUMS)
|
||||
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..."
|
||||
@@ -501,9 +636,9 @@ help: ## show this help screen
|
||||
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
@echo ''
|
||||
|
||||
funcgen: lang/funcs/core/generated_funcs.go
|
||||
funcgen: lang/core/generated_funcs.go
|
||||
|
||||
lang/funcs/core/generated_funcs.go: lang/funcs/funcgen/*.go lang/funcs/core/funcgen.yaml lang/funcs/funcgen/templates/generated_funcs.go.tpl
|
||||
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
|
||||
|
||||
|
||||
13
README.md
13
README.md
@@ -3,9 +3,9 @@
|
||||
[](art/)
|
||||
|
||||
[](https://goreportcard.com/report/github.com/purpleidea/mgmt)
|
||||
[](http://travis-ci.org/purpleidea/mgmt)
|
||||
[](https://github.com/purpleidea/mgmt/actions/)
|
||||
[](https://godoc.org/github.com/purpleidea/mgmt)
|
||||
[](https://godocs.io/github.com/purpleidea/mgmt)
|
||||
[](https://matrix.to/#/#mgmtconfig:matrix.org)
|
||||
[](https://web.libera.chat/?channels=#mgmtconfig)
|
||||
[](https://www.patreon.com/purpleidea)
|
||||
[](https://liberapay.com/purpleidea/donate)
|
||||
@@ -66,11 +66,11 @@ Come join us in the `mgmt` community!
|
||||
|
||||
| Medium | Link |
|
||||
|---|---|
|
||||
| Matrix | [#mgmtconfig](https://matrix.to/#/#mgmtconfig:matrix.org) on Matrix.org |
|
||||
| IRC | [#mgmtconfig](https://web.libera.chat/?channels=#mgmtconfig) on Libera.Chat |
|
||||
| Twitter | [@mgmtconfig](https://twitter.com/mgmtconfig) & [#mgmtconfig](https://twitter.com/hashtag/mgmtconfig) |
|
||||
| Mailing list | [mgmtconfig-list@redhat.com](https://www.redhat.com/mailman/listinfo/mgmtconfig-list) |
|
||||
| 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 |
|
||||
| Liberapay | [purpleidea](https://liberapay.com/purpleidea/donate) on Liberapay |
|
||||
|
||||
## Status:
|
||||
|
||||
@@ -98,8 +98,9 @@ Please read, enjoy and help improve our documentation!
|
||||
| [style guide](docs/style-guide.md) | for mgmt 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:
|
||||
|
||||
@@ -128,6 +129,6 @@ We'd love to have your patches! Please send them by email, or as a pull request.
|
||||
|
||||
## On the web:
|
||||
|
||||
[Read what people are saying and publishing about mgmt!](docs/on-the-web.md)
|
||||
[Blog posts and recorded talks about mgmt are listed here!](docs/on-the-web.md)
|
||||
|
||||
Happy hacking!
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
# Mgmt
|
||||
# Copyright (C) 2013-2021+ 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# The bindata target generates go files from any source defined below. To use
|
||||
# the files, import the generated "bindata" package and use:
|
||||
# `bytes, err := bindata.Asset("FILEPATH")`
|
||||
# where FILEPATH is the path of the original input file relative to `bindata/`.
|
||||
# To get a list of files stored in this "bindata" package, you can use:
|
||||
# `paths := bindata.AssetNames()` and `paths, err := bindata.AssetDir(name)`
|
||||
# to get a list of files with a directory prefix.
|
||||
|
||||
.PHONY: build clean
|
||||
default: build
|
||||
|
||||
build: bindata.go
|
||||
|
||||
# add more input files as dependencies at the end here...
|
||||
bindata.go: ../COPYING
|
||||
@echo "Generating: bindata..."
|
||||
# go-bindata --pkg bindata -o <OUTPUT> <INPUT>
|
||||
go-bindata --pkg bindata -o ./$@ $^
|
||||
# gofmt the output file
|
||||
gofmt -s -w $@
|
||||
@ROOT=$$(dirname "$${BASH_SOURCE}")/.. && $$ROOT/misc/header.sh '$@'
|
||||
|
||||
clean:
|
||||
# remove generated bindata.go
|
||||
@ROOT=$$(dirname "$${BASH_SOURCE}")/.. && rm -f bindata.go
|
||||
165
cli/cli.go
Normal file
165
cli/cli.go
Normal file
@@ -0,0 +1,165 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2024+ 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"`
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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{}
|
||||
255
cli/deploy.go
Normal file
255
cli/deploy.go
Normal file
@@ -0,0 +1,255 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2024+ 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/etcd/client"
|
||||
"github.com/purpleidea/mgmt/etcd/deployer"
|
||||
etcdfs "github.com/purpleidea/mgmt/etcd/fs"
|
||||
"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 {
|
||||
Seeds []string `arg:"--seeds,env:MGMT_SEEDS" help:"default etc client endpoint"`
|
||||
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"`
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}()
|
||||
|
||||
simpleDeploy := &deployer.SimpleDeploy{
|
||||
Client: etcdClient,
|
||||
Debug: data.Flags.Debug,
|
||||
Logf: func(format string, v ...interface{}) {
|
||||
Logf("deploy: "+format, v...)
|
||||
},
|
||||
}
|
||||
if err := simpleDeploy.Init(); err != nil {
|
||||
return false, errwrap.Wrapf(err, "deploy Init failed")
|
||||
}
|
||||
defer func() {
|
||||
err := errwrap.Wrapf(simpleDeploy.Close(), "deploy Close failed")
|
||||
if err != nil {
|
||||
// TODO: cause the final exit code to be non-zero
|
||||
Logf("deploy cleanup error: %+v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// get max id (from all the previous deploys)
|
||||
max, err := simpleDeploy.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)
|
||||
|
||||
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
|
||||
|
||||
str, err := deploy.ToB64()
|
||||
if err != nil {
|
||||
return false, errwrap.Wrapf(err, "encoding error")
|
||||
}
|
||||
|
||||
// this nominally checks the previous git hash matches our expectation
|
||||
if err := simpleDeploy.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
|
||||
}
|
||||
233
cli/run.go
Normal file
233
cli/run.go
Normal file
@@ -0,0 +1,233 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2024+ 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"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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,
|
||||
},
|
||||
|
||||
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
|
||||
}
|
||||
104
cli/util/args.go
Normal file
104
cli/util/args.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2024+ 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{}
|
||||
|
||||
// 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" 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"`
|
||||
}
|
||||
47
cli/util/hello.go
Normal file
47
cli/util/hello.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2024+ 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) 2013-2024+ 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) 2013-2024+ 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,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
@@ -29,7 +41,7 @@ import (
|
||||
)
|
||||
|
||||
// New builds a new converger coordinator.
|
||||
func New(timeout int64) *Coordinator {
|
||||
func New(timeout int) *Coordinator {
|
||||
return &Coordinator{
|
||||
timeout: timeout,
|
||||
|
||||
@@ -61,7 +73,7 @@ func New(timeout int64) *Coordinator {
|
||||
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 int64
|
||||
timeout int
|
||||
|
||||
// mutex is used for controlling access to status and lastid.
|
||||
mutex *sync.RWMutex
|
||||
@@ -365,7 +377,7 @@ func (obj *Coordinator) Status() map[*UID]bool {
|
||||
// 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 Coordinator struct.
|
||||
func (obj *Coordinator) Timeout() int64 {
|
||||
func (obj *Coordinator) Timeout() int {
|
||||
return obj.timeout
|
||||
}
|
||||
|
||||
@@ -375,7 +387,7 @@ func (obj *Coordinator) Timeout() int64 {
|
||||
type UID struct {
|
||||
// timeout is a copy of the main timeout. It could eventually be used
|
||||
// for per-UID timeouts too.
|
||||
timeout int64
|
||||
timeout int
|
||||
// isConverged stores the convergence state of this particular UID.
|
||||
isConverged bool
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,9 +13,21 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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.
|
||||
|
||||
// +build !root
|
||||
//go:build !root
|
||||
|
||||
package converger
|
||||
|
||||
|
||||
14
debian/copyright
vendored
14
debian/copyright
vendored
@@ -3,7 +3,7 @@ Upstream-Name: mgmt
|
||||
Source: <https://github.com/purpleidea/mgmt>
|
||||
|
||||
Files: *
|
||||
Copyright: Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
Copyright: Copyright (C) 2013-2024+ James Shubin and the project contributors
|
||||
License: GPL-3.0
|
||||
|
||||
License: GPL-3.0
|
||||
@@ -19,3 +19,15 @@ License: GPL-3.0
|
||||
|
||||
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.
|
||||
|
||||
16
doc.go
16
doc.go
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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,4 +1,4 @@
|
||||
FROM golang:1.13
|
||||
FROM golang:1.20
|
||||
|
||||
MAINTAINER Michał Czeraszkiewicz <contact@czerasz.com>
|
||||
|
||||
|
||||
20
docker/Dockerfile-fedora.build
Normal file
20
docker/Dockerfile-fedora.build
Normal file
@@ -0,0 +1,20 @@
|
||||
FROM fedora:38
|
||||
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"]
|
||||
@@ -6,7 +6,7 @@ ENV PATH=/opt/rh/rh-ruby22/root/usr/bin:/root/gopath/bin:/usr/local/sbin:/sbin:/
|
||||
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.9.1.linux-amd64.tar.gz https://storage.googleapis.com/golang/go1.9.1.linux-amd64.tar.gz && tar -C /usr/local -xzf /opt/go1.9.1.linux-amd64.tar.gz
|
||||
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.20.11.linux-amd64.tar.gz https://storage.googleapis.com/golang/go1.20.11.linux-amd64.tar.gz && tar -C /usr/local -xzf /opt/go1.20.11.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,4 +1,4 @@
|
||||
FROM golang:1.13
|
||||
FROM golang:1.20
|
||||
|
||||
MAINTAINER Michał Czeraszkiewicz <contact@czerasz.com>
|
||||
|
||||
@@ -27,8 +27,5 @@ WORKDIR /home/$USER_NAME/mgmt
|
||||
# Install dependencies
|
||||
RUN make deps
|
||||
|
||||
# Chown $GOPATH
|
||||
RUN chown -R ${USER_ID}:${GROUP_ID} /go
|
||||
|
||||
# Change user
|
||||
USER ${USER_NAME}
|
||||
|
||||
@@ -51,7 +51,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'mgmt'
|
||||
copyright = u'2013-2021+ James Shubin and the project contributors'
|
||||
copyright = u'2013-2024+ James Shubin and the project contributors'
|
||||
author = u'James Shubin'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
||||
@@ -28,7 +28,7 @@ required for running the _test_ suite.
|
||||
|
||||
### Build
|
||||
|
||||
* `golang` 1.13 or higher (required, available in some distros and distributed
|
||||
* `golang` 1.20 or higher (required, available in some distros and distributed
|
||||
as a binary officially by [golang.org](https://golang.org/dl/))
|
||||
|
||||
### Runtime
|
||||
|
||||
@@ -131,33 +131,6 @@ execute via a `remote` resource.
|
||||
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 Master (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
|
||||
@@ -364,27 +337,11 @@ 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`.
|
||||
|
||||
#### `--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
|
||||
@@ -486,7 +443,7 @@ To report any bugs, please file a ticket at: [https://github.com/purpleidea/mgmt
|
||||
|
||||
## Authors
|
||||
|
||||
Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
Copyright (C) 2013-2024+ James Shubin and the project contributors
|
||||
|
||||
Please see the
|
||||
[AUTHORS](https://github.com/purpleidea/mgmt/tree/master/AUTHORS) file
|
||||
|
||||
105
docs/faq.md
105
docs/faq.md
@@ -216,16 +216,41 @@ requires a number of seconds as an argument.
|
||||
./mgmt run lang examples/lang/hello0.mcl --converged-timeout=5
|
||||
```
|
||||
|
||||
### When I try to build `mgmt` I see: `no Go files in $GOPATH/src/github.com/purpleidea/mgmt/bindata`.
|
||||
### Can I run `mgmt` for type-checking only?
|
||||
|
||||
Due to the arcane way that `golang` designed its `$GOPATH`, the main project
|
||||
directory must be inside your `$GOPATH`, and at the appropriate FQDN. This is:
|
||||
`$GOPATH/src/github.com/purpleidea/mgmt/`. If you have your project root outside
|
||||
of that directory, then you may get this error when you try to build it. In this
|
||||
case there is likely a `go get` version of the project at this location. Remove
|
||||
it and replace it with your git cloned directory. In my case, I like to work on
|
||||
things in `~/code/mgmt/`, so that path is a symlink that points to the long
|
||||
project directory.
|
||||
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`?
|
||||
|
||||
@@ -253,7 +278,7 @@ 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 reversable file resource.
|
||||
remove an ambiguous scenario with the reversible file resource.
|
||||
|
||||
### Why do function names inside of templates include underscores?
|
||||
|
||||
@@ -295,6 +320,58 @@ 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: "could not unify types: 2 unconsumed generators".
|
||||
|
||||
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}",
|
||||
}
|
||||
```
|
||||
|
||||
Yes we know the compiler gives horrible error messages, and yes we would
|
||||
absolutely love your help improving this.
|
||||
|
||||
### 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
|
||||
@@ -364,6 +441,14 @@ 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
|
||||
|
||||
@@ -40,9 +40,9 @@ 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/funcs/core/`](https://github.com/purpleidea/mgmt/tree/master/lang/funcs/core/).
|
||||
The function should be implemented as a `FuncValue` in our type system. It is
|
||||
then registered with the engine during `init()`. An example explains it best:
|
||||
[`lang/core/`](https://github.com/purpleidea/mgmt/tree/master/lang/core/). The
|
||||
function should be implemented as a `FuncValue` in our type system. It is then
|
||||
registered with the engine during `init()`. An example explains it best:
|
||||
|
||||
### Example
|
||||
|
||||
@@ -115,11 +115,11 @@ re-evaluated as needed when their input values change.
|
||||
To implement a function, you'll need to create a file that imports the
|
||||
[`lang/funcs/simplepoly/`](https://github.com/purpleidea/mgmt/tree/master/lang/funcs/simplepoly/)
|
||||
module. It should probably get created in the correct directory inside of:
|
||||
[`lang/funcs/core/`](https://github.com/purpleidea/mgmt/tree/master/lang/funcs/core/).
|
||||
The function should be implemented as a list of `FuncValue`'s in our type
|
||||
system. It is then registered with the engine during `init()`. You may also use
|
||||
the `variant` type in your type definitions. This special type will never be
|
||||
seen inside a running program, and will get converted to a concrete type if a
|
||||
[`lang/core/`](https://github.com/purpleidea/mgmt/tree/master/lang/core/). The
|
||||
function should be implemented as a list of `FuncValue`'s in our type system. It
|
||||
is then registered with the engine during `init()`. You may also use the
|
||||
`variant` type in your type definitions. This special type will never be seen
|
||||
inside a running program, and will get converted to a concrete type if a
|
||||
suitable match to this signature can be found. Be warned that signatures which
|
||||
contain too many variants, or which are very general, might be hard for the
|
||||
compiler to match, and ambiguous type graphs make for user compiler errors. The
|
||||
@@ -239,27 +239,6 @@ use in the other methods.
|
||||
// Init runs some startup code for this function.
|
||||
func (obj *FooFunc) Init(init *interfaces.Init) error {
|
||||
obj.init = init
|
||||
obj.closeChan = make(chan struct{}) // shutdown signal
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Close
|
||||
|
||||
```golang
|
||||
Close() error
|
||||
```
|
||||
|
||||
This is called to cleanup the function. It usually causes the stream to
|
||||
shutdown. Even if `Stream()` decided to shutdown early, it might still get
|
||||
called. It is usually called by the engine to tell the function to shutdown.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Close runs some shutdown code for this function and turns off the stream.
|
||||
func (obj *FooFunc) Close() error {
|
||||
close(obj.closeChan) // send a signal to tell the stream to close
|
||||
return nil
|
||||
}
|
||||
```
|
||||
@@ -267,23 +246,24 @@ func (obj *FooFunc) Close() error {
|
||||
### Stream
|
||||
|
||||
```golang
|
||||
Stream() error
|
||||
Stream(context.Context) error
|
||||
```
|
||||
|
||||
`Stream` is where the real _work_ is done. This method is started by the
|
||||
language function engine. It will run this function while simultaneously sending
|
||||
it values on the `input` channel. It will only send a complete set of input
|
||||
it values on the `Input` channel. It will only send a complete set of input
|
||||
values. You should send a value to the output channel when you have decided that
|
||||
one should be produced. Make sure to only use input values of the expected type
|
||||
as declared in the `Info` struct, and send values of the similarly declared
|
||||
appropriate return type. Failure to do so will may result in a panic and
|
||||
sadness.
|
||||
sadness. You must shutdown if the input context cancels. You must close the
|
||||
`Output` channel if you are done generating new values and/or when you shutdown.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Stream returns the single value that was generated and then closes.
|
||||
func (obj *FooFunc) Stream() error {
|
||||
func (obj *FooFunc) Stream(ctx context.Context) error {
|
||||
defer close(obj.init.Output) // the sender closes
|
||||
var result string
|
||||
for {
|
||||
@@ -300,7 +280,7 @@ func (obj *FooFunc) Stream() error {
|
||||
|
||||
result = fmt.Sprintf("the input is: %d", ix)
|
||||
|
||||
case <-obj.closeChan:
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -309,7 +289,7 @@ func (obj *FooFunc) Stream() error {
|
||||
V: result,
|
||||
}:
|
||||
|
||||
case <-obj.closeChan:
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -340,8 +320,6 @@ type FooFunc struct {
|
||||
init *interfaces.Init
|
||||
|
||||
// this space can be used if needed
|
||||
|
||||
closeChan chan struct{} // shutdown signal
|
||||
}
|
||||
```
|
||||
|
||||
@@ -364,7 +342,9 @@ 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{} })
|
||||
funcs.ModuleRegister(moduleName, "cos", func() interfaces.Func {
|
||||
return &CosFunc{}
|
||||
})
|
||||
```
|
||||
|
||||
### Composite functions
|
||||
|
||||
@@ -14,4 +14,3 @@ Welcome to mgmt's documentation!
|
||||
quick-start-guide
|
||||
resource-guide
|
||||
prometheus
|
||||
puppet-guide
|
||||
|
||||
@@ -402,6 +402,38 @@ time.
|
||||
Recursive classes are not currently supported and it is not clear if they will
|
||||
be in the future. Discussion about this topic is welcome on the mailing list.
|
||||
|
||||
A class names can contain colons to indicate it is nested inside of the class in
|
||||
the same scope which is named with the prefix indicated by colon separation.
|
||||
Instead of needing to repeatedly indent the child classes, we can instead prefix
|
||||
them at the definition site (where created with the class keyword) with the name
|
||||
of the parent class, followed by a colon, to get the desired embedded sugar.
|
||||
|
||||
For example, instead of writing:
|
||||
|
||||
```mcl
|
||||
class base() {
|
||||
class inner() {
|
||||
class deepest() {
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can instead write:
|
||||
|
||||
```mcl
|
||||
class base() {
|
||||
}
|
||||
class base:inner() {
|
||||
}
|
||||
class base:inner:deepest() {
|
||||
}
|
||||
```
|
||||
|
||||
Of course, you can only access any of the inner classes by first including
|
||||
(with the include keyword) a parent class, and then subsequently including the
|
||||
inner one.
|
||||
|
||||
#### Include
|
||||
|
||||
The `include` statement causes the previously defined class to produce the
|
||||
@@ -414,6 +446,57 @@ parameters, then the same class can even be called with different signatures.
|
||||
Whether the output is useful and whether there is a unique type unification
|
||||
solution is dependent on your code.
|
||||
|
||||
Classes can be included under a new scoped prefix by using the `as` field and an
|
||||
identifier. When used in this manner, the captured scope of the class at its
|
||||
definition site are made available in the scope of the include. Variables,
|
||||
functions, and child classes are all exported.
|
||||
|
||||
Variables are available in the include scope:
|
||||
|
||||
```mcl
|
||||
import "fmt"
|
||||
|
||||
class c1 {
|
||||
test "t1" {} # gets pulled out
|
||||
$x = "hello" # gets exported
|
||||
}
|
||||
include c1 as i1
|
||||
|
||||
test "print0" {
|
||||
anotherstr => fmt.printf("%s", $i1.x), # hello
|
||||
onlyshow => ["AnotherStr",], # displays nicer
|
||||
}
|
||||
```
|
||||
|
||||
Classes are also available in the new scope:
|
||||
|
||||
```mcl
|
||||
import "fmt"
|
||||
class c1 {
|
||||
test "t1" {} # gets pulled out
|
||||
$x = "hello" # gets exported
|
||||
|
||||
class c0 {
|
||||
test "t2" {}
|
||||
$x = "goodbye"
|
||||
}
|
||||
}
|
||||
include c1 as i1
|
||||
include i1.c0 as i0
|
||||
|
||||
test "print0" {
|
||||
anotherstr => fmt.printf("%s", $i1.x), # hello
|
||||
onlyshow => ["AnotherStr",], # displays nicer
|
||||
}
|
||||
test "print1" {
|
||||
anotherstr => fmt.printf("%s", $i0.x), # goodbye
|
||||
onlyshow => ["AnotherStr",], # displays nicer
|
||||
}
|
||||
```
|
||||
|
||||
Of course classes can be parameterized too, and those variables specified during
|
||||
the `include`.
|
||||
|
||||
#### Import
|
||||
|
||||
The `import` statement imports a scope into the specified namespace. A scope can
|
||||
@@ -502,6 +585,10 @@ and can be used for other scenarios in which one statement or expression would
|
||||
be better represented by a larger AST. Most nodes in the AST simply return their
|
||||
own node address, and do not modify the AST.
|
||||
|
||||
This stage also implements the class nesting when it finds class names with
|
||||
colons that should be nested inside of a base class. Currently this does modify
|
||||
the AST for efficiency and simplicity.
|
||||
|
||||
#### Scope propagation
|
||||
|
||||
Scope propagation passes the parent scope (starting with the top-level, built-in
|
||||
@@ -563,11 +650,11 @@ would like to propose a more logical or performant variant.
|
||||
|
||||
#### Function graph generation
|
||||
|
||||
At this point we have a fully type AST. The AST must now be transformed into a
|
||||
At this point we have a fully typed AST. The AST must now be transformed into a
|
||||
directed, acyclic graph (DAG) data structure that represents the flow of data as
|
||||
necessary for everything to be reactive. Note that this graph is *different*
|
||||
from the resource graph which is produced and sent to the engine. It is just a
|
||||
coincidence that both happen to be DAG's. (You don't freak out when you see a
|
||||
coincidence that both happen to be DAG's. (You aren't surprised when you see a
|
||||
list data structure show up in more than one place, do you?)
|
||||
|
||||
To produce this graph, each node has a `Graph` method which it can call. This
|
||||
@@ -575,9 +662,8 @@ starts at the top most node, and is called down through the AST. The edges in
|
||||
the graphs must represent the individual expression values which are passed
|
||||
from node to node. The names of the edges must match the function type argument
|
||||
names which are used in the definition of the corresponding function. These
|
||||
corresponding functions must exist for each expression node and are produced by
|
||||
calling that expression's `Func` method. These are usually called by the
|
||||
function engine during function creation and validation.
|
||||
corresponding functions must exist for each expression node and are produced as
|
||||
the vertices of this returned graph. This is built for the function engine.
|
||||
|
||||
#### Function engine creation and validation
|
||||
|
||||
@@ -677,13 +763,13 @@ one value must be produced.
|
||||
|
||||
```golang
|
||||
Please see the example functions in
|
||||
[lang/funcs/core/](https://github.com/purpleidea/mgmt/tree/master/lang/funcs/core/).
|
||||
[lang/core/](https://github.com/purpleidea/mgmt/tree/master/lang/core/).
|
||||
```
|
||||
|
||||
### Stream
|
||||
|
||||
```golang
|
||||
Stream() error
|
||||
Stream(context.Context) error
|
||||
```
|
||||
|
||||
Stream is called by the function engine when it is ready for your function to
|
||||
@@ -692,29 +778,14 @@ value. Failure to produce at least one value will probably cause the function
|
||||
engine to hang waiting for your output. This function must close the `Output`
|
||||
channel when it has no more values to send. The engine will close the `Input`
|
||||
channel when it has no more values to send. This may or may not influence
|
||||
whether or not you close the `Output` channel.
|
||||
whether or not you close the `Output` channel. You must shutdown if the input
|
||||
context cancels.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
Please see the example functions in
|
||||
[lang/funcs/core/](https://github.com/purpleidea/mgmt/tree/master/lang/funcs/core/).
|
||||
```
|
||||
|
||||
### Close
|
||||
|
||||
```golang
|
||||
Close() error
|
||||
```
|
||||
|
||||
Close asks the particular function to shutdown its `Stream()` function and
|
||||
return.
|
||||
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
Please see the example functions in
|
||||
[lang/funcs/core/](https://github.com/purpleidea/mgmt/tree/master/lang/funcs/core/).
|
||||
[lang/core/](https://github.com/purpleidea/mgmt/tree/master/lang/core/).
|
||||
```
|
||||
|
||||
### Polymorphic Function API
|
||||
|
||||
@@ -56,3 +56,6 @@ if we missed something that you think is relevant!
|
||||
| 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) |
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
# 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://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:
|
||||
|
||||
```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://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 --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.
|
||||
@@ -21,6 +21,8 @@ to build your own.
|
||||
|
||||
### Downloading a pre-built release:
|
||||
|
||||
This method is not recommended because those packages are now very old.
|
||||
|
||||
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/).
|
||||
|
||||
@@ -37,7 +39,7 @@ You'll need some dependencies, including `golang`, and some associated tools.
|
||||
|
||||
#### Installing golang
|
||||
|
||||
* You need golang version 1.13 or greater installed.
|
||||
* You need golang version 1.20 or greater 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)
|
||||
@@ -63,13 +65,11 @@ export GOPATH=$HOME/gopath
|
||||
|
||||
#### Getting the mgmt code and associated dependencies
|
||||
|
||||
* Download the `mgmt` code into the `GOPATH`, and switch to that directory:
|
||||
* Download the `mgmt` code and switch to that directory:
|
||||
|
||||
```shell
|
||||
[ -z "$GOPATH" ] && mkdir ~/go/ || mkdir -p $GOPATH/src/github.com/purpleidea/
|
||||
cd $GOPATH/src/github.com/purpleidea/ || cd ~/go/
|
||||
git clone --recursive https://github.com/purpleidea/mgmt/
|
||||
cd $GOPATH/src/github.com/purpleidea/mgmt/ || cd ~/go/src/github.com/purpleidea/mgmt/
|
||||
git clone --recursive https://github.com/purpleidea/mgmt/ ~/mgmt/
|
||||
cd ~/mgmt/
|
||||
```
|
||||
|
||||
* Add `$GOPATH/bin` to `$PATH`
|
||||
@@ -97,6 +97,21 @@ At the moment we have:
|
||||
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/ ~/mgmt/
|
||||
cd ~/mgmt/docker
|
||||
buildah build -f Dockerfile-fedora.build -t mgmt_build
|
||||
podman run -d -it --name mgmt_build localhost/mgmt_build
|
||||
podman cp mgmt_build:/src/github.com/purpleidea/mgmt/mgmt /tmp/mgmt
|
||||
sudo mv /tmp/mgmt /usr/local/bin # be sure this is in your $PATH
|
||||
sudo chown root:root /usr/local/bin/mgmt
|
||||
```
|
||||
|
||||
## Running mgmt
|
||||
|
||||
* Run `mgmt run --tmp-prefix lang examples/lang/hello0.mcl` to try out a very
|
||||
|
||||
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
|
||||
behviour 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
|
||||
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
|
||||
@@ -60,10 +60,7 @@ 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.
|
||||
then you'll need to include the appropriate YAML fields as shown below.
|
||||
|
||||
#### Example
|
||||
|
||||
@@ -177,10 +174,10 @@ 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!
|
||||
|
||||
### Close
|
||||
### Cleanup
|
||||
|
||||
```golang
|
||||
Close() error
|
||||
Cleanup() error
|
||||
```
|
||||
|
||||
This is called to cleanup after the resource. It is usually not necessary, but
|
||||
@@ -192,8 +189,8 @@ loop.
|
||||
#### Example
|
||||
|
||||
```golang
|
||||
// Close runs some cleanup code for this resource.
|
||||
func (obj *FooRes) Close() error {
|
||||
// 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
|
||||
@@ -206,7 +203,7 @@ on an error if something went wrong.
|
||||
### CheckApply
|
||||
|
||||
```golang
|
||||
CheckApply(apply bool) (checkOK bool, err error)
|
||||
CheckApply(ctx context.Context, apply bool) (checkOK bool, err error)
|
||||
```
|
||||
|
||||
`CheckApply` is where the real _work_ is done. Under normal circumstances, this
|
||||
@@ -215,7 +212,8 @@ 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!
|
||||
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
|
||||
@@ -234,7 +232,7 @@ to `CheckApply`.
|
||||
|
||||
```golang
|
||||
// CheckApply does the idempotent work of checking and applying resource state.
|
||||
func (obj *FooRes) CheckApply(apply bool) (bool, error) {
|
||||
func (obj *FooRes) CheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
// check the state
|
||||
if state_is_okay { return true, nil } // done early! :)
|
||||
|
||||
@@ -277,7 +275,7 @@ will likely find the state to now be correct.
|
||||
### Watch
|
||||
|
||||
```golang
|
||||
Watch() error
|
||||
Watch(ctx context.Context) error
|
||||
```
|
||||
|
||||
`Watch` is a main loop that runs and sends messages when it detects that the
|
||||
@@ -304,23 +302,25 @@ 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 `<-obj.init.Done` channel, which
|
||||
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 `<-obj.init.Done` channel closes, we should shutdown our resource. When
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -347,7 +347,7 @@ sending out erroneous `Event` messages to keep things alive until it finishes.
|
||||
|
||||
```golang
|
||||
// Watch is the listener and main loop for this resource.
|
||||
func (obj *FooRes) Watch() error {
|
||||
func (obj *FooRes) Watch(ctx context.Context) error {
|
||||
// setup the Foo resource
|
||||
var err error
|
||||
if err, obj.foo = OpenFoo(); err != nil {
|
||||
@@ -371,7 +371,7 @@ func (obj *FooRes) Watch() error {
|
||||
case err := <-obj.foo.Errors:
|
||||
return err // will cause a retry or permanent failure
|
||||
|
||||
case <-obj.init.Done: // signal for shutdown request
|
||||
case <-ctx.Done(): // signal for shutdown request
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -553,11 +553,6 @@ ready to detect changes.
|
||||
Event sends an event notifying the engine of a possible state change. It is
|
||||
only called from within `Watch`.
|
||||
|
||||
### Done
|
||||
|
||||
Done is a channel that closes when the engine wants us to shutdown. It is only
|
||||
called from within `Watch`.
|
||||
|
||||
### Refresh
|
||||
|
||||
Refresh returns whether the resource received a notification. This flag can be
|
||||
@@ -582,15 +577,15 @@ It is only called from within `CheckApply`.
|
||||
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 `Close`.
|
||||
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 `Close` 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.
|
||||
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
|
||||
|
||||
@@ -625,7 +620,7 @@ func init() { // special golang method that runs once
|
||||
|
||||
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.
|
||||
`yaml` compiler.
|
||||
|
||||
```golang
|
||||
UnmarshalYAML(unmarshal func(interface{}) error) error // optional
|
||||
@@ -680,10 +675,10 @@ 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()["SomeKey"]; exists {
|
||||
obj.init.Logf("the SomeKey param was sent to us from: %s.%s", val.Res, val.Key)
|
||||
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 SomeKey param was just updated!")
|
||||
obj.init.Logf("the some_key param was just updated!")
|
||||
// you may want to invalidate some local cache
|
||||
}
|
||||
}
|
||||
@@ -727,16 +722,16 @@ 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 `Close`, however you might not want that open for the
|
||||
`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(apply bool) (bool, error) {
|
||||
func (obj *FooRes) CheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
// my private split implementation of check and apply
|
||||
if c, err := obj.check(); err != nil {
|
||||
if c, err := obj.check(ctx); err != nil {
|
||||
return false, err // we errored
|
||||
} else if c {
|
||||
return true, nil // state was good!
|
||||
@@ -746,7 +741,7 @@ func (obj *FooRes) CheckApply(apply bool) (bool, error) {
|
||||
return false, nil // state needs fixing, but apply is false
|
||||
}
|
||||
|
||||
err := obj.apply() // errors if failure or unable to apply
|
||||
err := obj.apply(ctx) // errors if failure or unable to apply
|
||||
|
||||
return false, err // always return false, with an optional error
|
||||
}
|
||||
|
||||
@@ -141,6 +141,24 @@ 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,9 +13,21 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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.
|
||||
|
||||
// +build !root
|
||||
//go:build !root
|
||||
|
||||
package engine
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
|
||||
@@ -42,7 +54,8 @@ type GroupableRes interface {
|
||||
// grouping. This usually needs to be unique to your resource.
|
||||
GroupCmp(res GroupableRes) error
|
||||
|
||||
// GroupRes groups resource argument (res) into self.
|
||||
// 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.
|
||||
@@ -54,8 +67,15 @@ type GroupableRes interface {
|
||||
// GetGroup returns everyone grouped inside me.
|
||||
GetGroup() []GroupableRes // return everyone grouped inside me
|
||||
|
||||
// SetGroup sets the grouped resources into 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.
|
||||
|
||||
148
engine/cmp.go
148
engine/cmp.go
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
|
||||
@@ -21,6 +33,7 @@ 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
|
||||
@@ -106,6 +119,7 @@ func ResCmp(r1, r2 Res) error {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -121,14 +135,29 @@ func ResCmp(r1, r2 Res) error {
|
||||
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")
|
||||
//return fmt.Errorf("recv params differ")
|
||||
}
|
||||
if v1 != nil && v2 != nil {
|
||||
// TODO: until we hit this code path, don't allow
|
||||
// comparing anything that has this set to non-zero
|
||||
if len(v1) != 0 || len(v2) != 0 {
|
||||
return fmt.Errorf("recv params exist")
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,13 +171,17 @@ func ResCmp(r1, r2 Res) error {
|
||||
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")
|
||||
//return fmt.Errorf("send params differ")
|
||||
}
|
||||
if s1 != nil && s2 != nil {
|
||||
// TODO: until we hit this code path, don't allow
|
||||
// adapting anything that has this set to non-nil
|
||||
return fmt.Errorf("send params exist")
|
||||
// TODO: reflect.DeepEqual?
|
||||
//return fmt.Errorf("send params exist")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,14 +294,29 @@ func AdaptCmp(r1, r2 CompatibleRes) error {
|
||||
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")
|
||||
//return fmt.Errorf("recv params differ")
|
||||
}
|
||||
if v1 != nil && v2 != nil {
|
||||
// TODO: until we hit this code path, don't allow
|
||||
// adapting anything that has this set to non-zero
|
||||
if len(v1) != 0 || len(v2) != 0 {
|
||||
return fmt.Errorf("recv params exist")
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,13 +330,17 @@ func AdaptCmp(r1, r2 CompatibleRes) error {
|
||||
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")
|
||||
//return fmt.Errorf("send params differ")
|
||||
}
|
||||
if s1 != nil && s2 != nil {
|
||||
// TODO: until we hit this code path, don't allow
|
||||
// adapting anything that has this set to non-nil
|
||||
return fmt.Errorf("send params exist")
|
||||
// TODO: reflect.DeepEqual?
|
||||
//return fmt.Errorf("send params exist")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,9 +372,11 @@ func VertexCmpFn(v1, v2 pgraph.Vertex) (bool, error) {
|
||||
return false, fmt.Errorf("v2 is not a Res")
|
||||
}
|
||||
|
||||
if ResCmp(r1, r2) != nil {
|
||||
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
|
||||
}
|
||||
@@ -341,3 +395,55 @@ func EdgeCmpFn(e1, e2 pgraph.Edge) (bool, error) {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
|
||||
@@ -81,6 +93,7 @@ func ResCopy(r CopyableRes) (CopyableRes, error) {
|
||||
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)
|
||||
|
||||
33
engine/doc.go
Normal file
33
engine/doc.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
|
||||
@@ -26,4 +38,7 @@ func (e Error) Error() string { return string(e) }
|
||||
const (
|
||||
// ErrClosed means we couldn't complete a task because we had closed.
|
||||
ErrClosed = Error("closed")
|
||||
|
||||
// ErrBackPoke means we're postponing due to a needed backpoke.
|
||||
ErrBackPoke = Error("backpoke")
|
||||
)
|
||||
|
||||
101
engine/fs.go
101
engine/fs.go
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
|
||||
@@ -23,39 +35,64 @@ import (
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
// from the ioutil package:
|
||||
// NopCloser(r io.Reader) io.ReadCloser // not implemented here
|
||||
// ReadAll(r io.Reader) ([]byte, error)
|
||||
// ReadDir(dirname string) ([]os.FileInfo, error)
|
||||
// ReadFile(filename string) ([]byte, error)
|
||||
// TempDir(dir, prefix string) (name string, err error)
|
||||
// TempFile(dir, prefix string) (f *os.File, err error) // slightly different here
|
||||
// WriteFile(filename string, data []byte, perm os.FileMode) error
|
||||
|
||||
// Fs is an interface that represents this file system API that we support.
|
||||
// TODO: this should be in the gapi package or elsewhere.
|
||||
// Fs is an interface that represents the file system API that we support.
|
||||
// TODO: rename this to FS for consistency with the io/fs.FS naming scheme
|
||||
type Fs interface {
|
||||
//fmt.Stringer // TODO: add this method?
|
||||
afero.Fs // TODO: why doesn't this interface exist in the os pkg?
|
||||
|
||||
// URI returns a unique string handle to access this filesystem.
|
||||
URI() string // returns the URI for this file system
|
||||
|
||||
//DirExists(path string) (bool, error)
|
||||
//Exists(path string) (bool, error)
|
||||
//FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error)
|
||||
//FileContainsBytes(filename string, subslice []byte) (bool, error)
|
||||
//FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string
|
||||
//GetTempDir(subPath string) string
|
||||
//IsDir(path string) (bool, error)
|
||||
//IsEmpty(path string) (bool, error)
|
||||
//NeuterAccents(s string) string
|
||||
//ReadAll(r io.Reader) ([]byte, error) // not needed, same as ioutil
|
||||
afero.Fs // TODO: why doesn't this interface exist in the os pkg?
|
||||
|
||||
// FS is the read-only filesystem interface from the io/fs.FS package.
|
||||
//fs.FS // io/fs.FS
|
||||
|
||||
// ReadDir reads the named directory and returns a list of directory
|
||||
// entries sorted by filename.
|
||||
//
|
||||
// This mimics the signature from io/fs.ReadDirFS and has the same docs.
|
||||
//
|
||||
// XXX: Not currently implemented because of legacy Afero.Fs above
|
||||
//ReadDir(name string) ([]fs.DirEntry, error) // io/fs.ReadDirFS
|
||||
|
||||
// ReadFile reads the named file and returns its contents. A successful
|
||||
// call returns a nil error, not io.EOF. (Because ReadFile reads the
|
||||
// whole file, the expected EOF from the final Read is not treated as an
|
||||
// error to be reported.)
|
||||
//
|
||||
// The caller is permitted to modify the returned byte slice. This
|
||||
// method should return a copy of the underlying data.
|
||||
//
|
||||
// This mimics the signature from io/fs.ReadFileFS and has the same
|
||||
// docs.
|
||||
ReadFile(name string) ([]byte, error) // io/fs.ReadFileFS
|
||||
|
||||
// Stat returns a FileInfo describing the file. If there is an error, it
|
||||
// should be of type *fs.PathError.
|
||||
//
|
||||
// This mimics the signature from io/fs.StatFS and has the same docs.
|
||||
//
|
||||
// XXX: Not currently implemented because of legacy Afero.Fs above
|
||||
//Stat(name string) (FileInfo, error) // io/fs.StatFS
|
||||
|
||||
// afero.Fs versions:
|
||||
|
||||
ReadDir(dirname string) ([]os.FileInfo, error)
|
||||
ReadFile(filename string) ([]byte, error)
|
||||
//SafeWriteReader(path string, r io.Reader) (err error)
|
||||
TempDir(dir, prefix string) (name string, err error)
|
||||
TempFile(dir, prefix string) (f afero.File, err error) // slightly different from upstream
|
||||
//UnicodeSanitize(s string) string
|
||||
//Walk(root string, walkFn filepath.WalkFunc) error
|
||||
WriteFile(filename string, data []byte, perm os.FileMode) error
|
||||
//WriteReader(path string, r io.Reader) (err error)
|
||||
}
|
||||
|
||||
// WriteableFS is our internal filesystem interface for filesystems we write to.
|
||||
// It can wrap whatever implementations we want.
|
||||
type WriteableFS interface {
|
||||
Fs
|
||||
|
||||
// WriteFile writes data to the named file, creating it if necessary. If
|
||||
// the file does not exist, WriteFile creates it with permissions perm
|
||||
// (before umask); otherwise WriteFile truncates it before writing,
|
||||
// without changing permissions. Since Writefile requires multiple
|
||||
// system calls to complete, a failure mid-operation can leave the file
|
||||
// in a partially written state.
|
||||
//
|
||||
// This mimics the internal os.WriteFile function and has the same docs.
|
||||
WriteFile(name string, data []byte, perm os.FileMode) error
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,17 +13,31 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
engineUtil "github.com/purpleidea/mgmt/engine/util"
|
||||
"github.com/purpleidea/mgmt/pgraph"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
|
||||
@@ -39,14 +53,18 @@ func (obj *Engine) OKTimestamp(vertex pgraph.Vertex) bool {
|
||||
// be bad.
|
||||
func (obj *Engine) BadTimestamps(vertex pgraph.Vertex) []pgraph.Vertex {
|
||||
vs := []pgraph.Vertex{}
|
||||
ts := obj.state[vertex].timestamp
|
||||
obj.state[vertex].mutex.RLock() // concurrent read start
|
||||
ts := obj.state[vertex].timestamp // race
|
||||
obj.state[vertex].mutex.RUnlock() // concurrent read end
|
||||
// these are all the vertices pointing TO vertex, eg: ??? -> vertex
|
||||
for _, v := range obj.graph.IncomingGraphVertices(vertex) {
|
||||
// If the vertex has a greater timestamp than any prerequisite,
|
||||
// then we can't run right now. If they're equal (eg: initially
|
||||
// with a value of 0) then we also can't run because we should
|
||||
// let our pre-requisites go first.
|
||||
t := obj.state[v].timestamp
|
||||
obj.state[v].mutex.RLock() // concurrent read start
|
||||
t := obj.state[v].timestamp // race
|
||||
obj.state[v].mutex.RUnlock() // concurrent read end
|
||||
if obj.Debug {
|
||||
obj.Logf("OKTimestamp: %d >= %d (%s): !%t", ts, t, v.String(), ts >= t)
|
||||
}
|
||||
@@ -59,7 +77,7 @@ func (obj *Engine) BadTimestamps(vertex pgraph.Vertex) []pgraph.Vertex {
|
||||
}
|
||||
|
||||
// Process is the primary function to execute a particular vertex in the graph.
|
||||
func (obj *Engine) Process(vertex pgraph.Vertex) error {
|
||||
func (obj *Engine) Process(ctx context.Context, vertex pgraph.Vertex) error {
|
||||
res, isRes := vertex.(engine.Res)
|
||||
if !isRes {
|
||||
return fmt.Errorf("vertex is not a Res")
|
||||
@@ -83,7 +101,9 @@ func (obj *Engine) Process(vertex pgraph.Vertex) error {
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
return nil // can't continue until timestamp is in sequence
|
||||
|
||||
// can't continue until timestamp is in sequence, defer for now
|
||||
return engine.ErrBackPoke
|
||||
}
|
||||
|
||||
// semaphores!
|
||||
@@ -108,21 +128,41 @@ func (obj *Engine) Process(vertex pgraph.Vertex) error {
|
||||
|
||||
// sendrecv!
|
||||
// connect any senders to receivers and detect if values changed
|
||||
// this actually checks and sends into resource trees recursively...
|
||||
if res, ok := vertex.(engine.RecvableRes); ok {
|
||||
if updated, err := obj.SendRecv(res); err != nil {
|
||||
if obj.Debug {
|
||||
obj.Logf("SendRecv: %s", res) // receiving here
|
||||
}
|
||||
if updated, err := SendRecv(res, nil); err != nil {
|
||||
return errwrap.Wrapf(err, "could not SendRecv")
|
||||
} else if len(updated) > 0 {
|
||||
for _, changed := range updated {
|
||||
if changed { // at least one was updated
|
||||
// invalidate cache, mark as dirty
|
||||
obj.state[vertex].tuid.StopTimer()
|
||||
obj.state[vertex].isStateOK = false
|
||||
break
|
||||
//for _, s := range graph.UpdatedStrings(updated) {
|
||||
// obj.Logf("SendRecv: %s", s)
|
||||
//}
|
||||
for r, m := range updated { // map[engine.RecvableRes]map[string]*engine.Send
|
||||
v, ok := r.(pgraph.Vertex)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
_, stateExists := obj.state[v] // autogrouped children probably don't have a state
|
||||
if !stateExists {
|
||||
continue
|
||||
}
|
||||
for s, send := range m {
|
||||
if !send.Changed {
|
||||
continue
|
||||
}
|
||||
obj.Logf("Send/Recv: %v.%s -> %v.%s", send.Res, send.Key, r, s)
|
||||
// if send.Changed == true, at least one was updated
|
||||
// invalidate cache, mark as dirty
|
||||
obj.state[v].setDirty()
|
||||
//break // we might have more vertices now
|
||||
}
|
||||
|
||||
// re-validate after we change any values
|
||||
if err := engine.Validate(r); err != nil {
|
||||
return errwrap.Wrapf(err, "failed Validate after SendRecv")
|
||||
}
|
||||
}
|
||||
// re-validate after we change any values
|
||||
if err := engine.Validate(res); err != nil {
|
||||
return errwrap.Wrapf(err, "failed Validate after SendRecv")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,18 +184,18 @@ func (obj *Engine) Process(vertex pgraph.Vertex) error {
|
||||
// Check cached state, to skip CheckApply, but can't skip if refreshing!
|
||||
// If the resource doesn't implement refresh, skip the refresh test.
|
||||
// FIXME: if desired, check that we pass through refresh notifications!
|
||||
if (!refresh || !isRefreshableRes) && obj.state[vertex].isStateOK {
|
||||
if (!refresh || !isRefreshableRes) && obj.state[vertex].isStateOK.Load() { // mutex RLock/RUnlock
|
||||
checkOK, err = true, nil
|
||||
|
||||
} else if noop && (refresh && isRefreshableRes) { // had a refresh to do w/ noop!
|
||||
checkOK, err = false, nil // therefore the state is wrong
|
||||
|
||||
// run the CheckApply!
|
||||
} else {
|
||||
// run the CheckApply!
|
||||
obj.Logf("%s: CheckApply(%t)", res, !noop)
|
||||
// if this fails, don't UpdateTimestamp()
|
||||
checkOK, err = res.CheckApply(!noop)
|
||||
obj.Logf("%s: CheckApply(%t): Return(%t, %+v)", res, !noop, checkOK, err)
|
||||
checkOK, err = res.CheckApply(ctx, !noop)
|
||||
obj.Logf("%s: CheckApply(%t): Return(%t, %s)", res, !noop, checkOK, engineUtil.CleanError(err))
|
||||
}
|
||||
|
||||
if checkOK && err != nil { // should never return this way
|
||||
@@ -172,7 +212,9 @@ func (obj *Engine) Process(vertex pgraph.Vertex) error {
|
||||
// if CheckApply ran without noop and without error, state should be good
|
||||
if !noop && err == nil { // aka !noop || checkOK
|
||||
obj.state[vertex].tuid.StartTimer()
|
||||
obj.state[vertex].isStateOK = true // reset
|
||||
//obj.state[vertex].mutex.Lock()
|
||||
obj.state[vertex].isStateOK.Store(true) // reset
|
||||
//obj.state[vertex].mutex.Unlock()
|
||||
if refresh {
|
||||
obj.SetUpstreamRefresh(vertex, false) // refresh happened, clear the request
|
||||
if isRefreshableRes {
|
||||
@@ -209,7 +251,9 @@ func (obj *Engine) Process(vertex pgraph.Vertex) error {
|
||||
wg := &sync.WaitGroup{}
|
||||
// update this timestamp *before* we poke or the poked
|
||||
// nodes might fail due to having a too old timestamp!
|
||||
obj.state[vertex].timestamp = time.Now().UnixNano() // update timestamp
|
||||
obj.state[vertex].mutex.Lock() // concurrent write start
|
||||
obj.state[vertex].timestamp = time.Now().UnixNano() // update timestamp (race)
|
||||
obj.state[vertex].mutex.Unlock() // concurrent write end
|
||||
for _, v := range obj.graph.OutgoingGraphVertices(vertex) {
|
||||
if !obj.OKTimestamp(v) {
|
||||
// there is at least another one that will poke this...
|
||||
@@ -255,6 +299,16 @@ func (obj *Engine) Worker(vertex pgraph.Vertex) error {
|
||||
return fmt.Errorf("permanently limited (rate != Inf, burst = 0)")
|
||||
}
|
||||
|
||||
// initialize or reinitialize the meta state for this resource uid
|
||||
obj.mlock.Lock()
|
||||
if _, exists := obj.metas[engine.PtrUID(res)]; !exists || res.MetaParams().Reset {
|
||||
obj.metas[engine.PtrUID(res)] = &engine.MetaState{
|
||||
CheckApplyRetry: res.MetaParams().Retry, // lookup the retry value
|
||||
}
|
||||
}
|
||||
metas := obj.metas[engine.PtrUID(res)] // handle
|
||||
obj.mlock.Unlock()
|
||||
|
||||
//defer close(obj.state[vertex].stopped) // done signal
|
||||
|
||||
obj.state[vertex].cuid = obj.Converger.Register()
|
||||
@@ -272,7 +326,7 @@ func (obj *Engine) Worker(vertex pgraph.Vertex) error {
|
||||
defer close(obj.state[vertex].eventsChan) // we close this on behalf of res
|
||||
|
||||
// This is a close reverse-multiplexer. If any of the channels
|
||||
// close, then it will cause the doneChan to close. That way,
|
||||
// close, then it will cause the doneCtx to cancel. That way,
|
||||
// multiple different folks can send a close signal, without
|
||||
// every worrying about duplicate channel close panics.
|
||||
obj.state[vertex].wg.Add(1)
|
||||
@@ -284,12 +338,13 @@ func (obj *Engine) Worker(vertex pgraph.Vertex) error {
|
||||
case <-obj.state[vertex].processDone:
|
||||
case <-obj.state[vertex].watchDone:
|
||||
case <-obj.state[vertex].limitDone:
|
||||
case <-obj.state[vertex].retryDone:
|
||||
case <-obj.state[vertex].removeDone:
|
||||
case <-obj.state[vertex].eventsDone:
|
||||
}
|
||||
|
||||
// the main "done" signal gets activated here!
|
||||
close(obj.state[vertex].doneChan)
|
||||
obj.state[vertex].doneCtxCancel() // cancels doneCtx
|
||||
}()
|
||||
|
||||
var err error
|
||||
@@ -308,7 +363,7 @@ func (obj *Engine) Worker(vertex pgraph.Vertex) error {
|
||||
case <-timer.C: // the wait is over
|
||||
return errDelayExpired // special
|
||||
|
||||
case <-obj.state[vertex].init.Done:
|
||||
case <-obj.state[vertex].doneCtx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -319,13 +374,21 @@ func (obj *Engine) Worker(vertex pgraph.Vertex) error {
|
||||
}
|
||||
} else if interval := res.MetaParams().Poll; interval > 0 { // poll instead of watching :(
|
||||
obj.state[vertex].cuid.StartTimer()
|
||||
err = obj.state[vertex].poll(interval)
|
||||
err = obj.state[vertex].poll(obj.state[vertex].doneCtx, interval)
|
||||
obj.state[vertex].cuid.StopTimer() // clean up nicely
|
||||
} else {
|
||||
obj.state[vertex].cuid.StartTimer()
|
||||
obj.Logf("Watch(%s)", vertex)
|
||||
err = res.Watch() // run the watch normally
|
||||
obj.Logf("Watch(%s): Exited(%+v)", vertex, err)
|
||||
if obj.Debug {
|
||||
obj.Logf("%s: Watch...", vertex)
|
||||
}
|
||||
err = res.Watch(obj.state[vertex].doneCtx) // run the watch normally
|
||||
if obj.Debug {
|
||||
if s := engineUtil.CleanError(err); err != nil {
|
||||
obj.Logf("%s: Watch Error: %s", vertex, s)
|
||||
} else {
|
||||
obj.Logf("%s: Watch Exited...", vertex)
|
||||
}
|
||||
}
|
||||
obj.state[vertex].cuid.StopTimer() // clean up nicely
|
||||
}
|
||||
if err == nil { // || err == engine.ErrClosed
|
||||
@@ -359,13 +422,19 @@ func (obj *Engine) Worker(vertex pgraph.Vertex) error {
|
||||
|
||||
// If this exits cleanly, we must unblock the reverse-multiplexer.
|
||||
// I think this additional close is unnecessary, but it's not harmful.
|
||||
defer close(obj.state[vertex].eventsDone) // causes doneChan to close
|
||||
defer close(obj.state[vertex].eventsDone) // causes doneCtx to cancel
|
||||
limiter := rate.NewLimiter(res.MetaParams().Limit, res.MetaParams().Burst)
|
||||
var reserv *rate.Reservation
|
||||
var reterr error
|
||||
var failed bool // has Process permanently failed?
|
||||
var closed bool // has the resumeSignal channel closed?
|
||||
Loop:
|
||||
for { // process loop
|
||||
// This is the main select where things happen and where we exit
|
||||
// from. It's similar to the two "satellite" select's which we
|
||||
// might spend some time in if we're retrying or rate limiting.
|
||||
// This select is also the main event receiver and is also the
|
||||
// only place where we read from the poke channel.
|
||||
select {
|
||||
case err, ok := <-obj.state[vertex].eventsChan: // read from watch channel
|
||||
if !ok {
|
||||
@@ -376,7 +445,7 @@ Loop:
|
||||
// we then save so we can return it to the caller of us.
|
||||
if err != nil {
|
||||
failed = true
|
||||
close(obj.state[vertex].watchDone) // causes doneChan to close
|
||||
close(obj.state[vertex].watchDone) // causes doneCtx to cancel
|
||||
reterr = errwrap.Append(reterr, err) // permanent failure
|
||||
continue
|
||||
}
|
||||
@@ -394,9 +463,33 @@ Loop:
|
||||
obj.Logf("poke received")
|
||||
}
|
||||
reserv = nil // we didn't receive a real event here...
|
||||
}
|
||||
if failed { // don't Process anymore if we've already failed...
|
||||
continue Loop
|
||||
|
||||
case _, ok := <-obj.state[vertex].pauseSignal: // one message
|
||||
if !ok {
|
||||
obj.state[vertex].pauseSignal = nil
|
||||
continue // this is not a new pause message
|
||||
}
|
||||
// NOTE: If we allowed a doneCtx below to let us out
|
||||
// of the resumeSignal wait, then we could loop around
|
||||
// and run this again, causing a panic. Instead of this
|
||||
// being made safe with a sync.Once, we instead run a
|
||||
// close() call inside of the vertexRemoveFn function,
|
||||
// which should unblock resumeSignal so we can shutdown.
|
||||
|
||||
// we are paused now, and waiting for resume or exit...
|
||||
select {
|
||||
case _, ok := <-obj.state[vertex].resumeSignal: // channel closes
|
||||
if !ok {
|
||||
closed = true
|
||||
}
|
||||
// resumed!
|
||||
// pass through to allow a Process to try to run
|
||||
// TODO: consider adding this fast pause here...
|
||||
//if obj.fastPause {
|
||||
// obj.Logf("fast pausing on resume")
|
||||
// continue
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
// drop redundant pokes
|
||||
@@ -408,31 +501,8 @@ Loop:
|
||||
}
|
||||
}
|
||||
|
||||
// pause if one was requested...
|
||||
select {
|
||||
case <-obj.state[vertex].pauseSignal: // channel closes
|
||||
// NOTE: If we allowed a doneChan below to let us out
|
||||
// of the resumeSignal wait, then we could loop around
|
||||
// and run this again, causing a panic. Instead of this
|
||||
// being made safe with a sync.Once, we instead run a
|
||||
// Resume() call inside of the vertexRemoveFn function,
|
||||
// which should unblock it when we're going to need to.
|
||||
obj.state[vertex].pausedAck.Ack() // send ack
|
||||
// we are paused now, and waiting for resume or exit...
|
||||
select {
|
||||
case <-obj.state[vertex].resumeSignal: // channel closes
|
||||
// resumed!
|
||||
// pass through to allow a Process to try to run
|
||||
// TODO: consider adding this fast pause here...
|
||||
//if obj.fastPause {
|
||||
// obj.Logf("fast pausing on resume")
|
||||
// continue
|
||||
//}
|
||||
}
|
||||
default:
|
||||
// no pause requested, keep going...
|
||||
}
|
||||
if failed { // don't Process anymore if we've already failed...
|
||||
// don't Process anymore if we've already failed or shutdown...
|
||||
if failed || closed {
|
||||
continue Loop
|
||||
}
|
||||
|
||||
@@ -442,10 +512,13 @@ Loop:
|
||||
d = reserv.DelayFrom(time.Now())
|
||||
}
|
||||
if reserv != nil && d > 0 { // delay
|
||||
obj.state[vertex].init.Logf("limited (rate: %v/sec, burst: %d, next: %v)", res.MetaParams().Limit, res.MetaParams().Burst, d)
|
||||
obj.state[vertex].init.Logf("limited (rate: %v/sec, burst: %d, next: %dms)", res.MetaParams().Limit, res.MetaParams().Burst, d/time.Millisecond)
|
||||
timer := time.NewTimer(time.Duration(d) * time.Millisecond)
|
||||
LimitWait:
|
||||
for {
|
||||
// This "satellite" select doesn't need a poke
|
||||
// channel because we're already in "event
|
||||
// received" mode, and poke doesn't block.
|
||||
select {
|
||||
case <-timer.C: // the wait is over
|
||||
break LimitWait
|
||||
@@ -457,7 +530,7 @@ Loop:
|
||||
}
|
||||
if e != nil {
|
||||
failed = true
|
||||
close(obj.state[vertex].limitDone) // causes doneChan to close
|
||||
close(obj.state[vertex].limitDone) // causes doneCtx to cancel
|
||||
reterr = errwrap.Append(reterr, e) // permanent failure
|
||||
break LimitWait
|
||||
}
|
||||
@@ -466,19 +539,34 @@ Loop:
|
||||
}
|
||||
// TODO: does this get added in properly?
|
||||
limiter.ReserveN(time.Now(), 1) // one event
|
||||
|
||||
// this pause/resume block is the same as the upper main one
|
||||
case _, ok := <-obj.state[vertex].pauseSignal:
|
||||
if !ok {
|
||||
obj.state[vertex].pauseSignal = nil
|
||||
break LimitWait
|
||||
}
|
||||
select {
|
||||
case _, ok := <-obj.state[vertex].resumeSignal: // channel closes
|
||||
if !ok {
|
||||
closed = true
|
||||
}
|
||||
// resumed!
|
||||
}
|
||||
}
|
||||
}
|
||||
timer.Stop() // it's nice to cleanup
|
||||
obj.state[vertex].init.Logf("rate limiting expired!")
|
||||
}
|
||||
if failed { // don't Process anymore if we've already failed...
|
||||
// don't Process anymore if we've already failed or shutdown...
|
||||
if failed || closed {
|
||||
continue Loop
|
||||
}
|
||||
// end of limit delay
|
||||
|
||||
// retry...
|
||||
var err error
|
||||
var retry = res.MetaParams().Retry // lookup the retry value
|
||||
//var retry = res.MetaParams().Retry // lookup the retry value
|
||||
var delay uint64
|
||||
RetryLoop:
|
||||
for { // retry loop
|
||||
@@ -486,6 +574,10 @@ Loop:
|
||||
timer := time.NewTimer(time.Duration(delay) * time.Millisecond)
|
||||
RetryWait:
|
||||
for {
|
||||
// This "satellite" select doesn't need
|
||||
// a poke channel because we're already
|
||||
// in "event received" mode, and poke
|
||||
// doesn't block.
|
||||
select {
|
||||
case <-timer.C: // the wait is over
|
||||
break RetryWait
|
||||
@@ -497,7 +589,7 @@ Loop:
|
||||
}
|
||||
if e != nil {
|
||||
failed = true
|
||||
close(obj.state[vertex].limitDone) // causes doneChan to close
|
||||
close(obj.state[vertex].retryDone) // causes doneCtx to cancel
|
||||
reterr = errwrap.Append(reterr, e) // permanent failure
|
||||
break RetryWait
|
||||
}
|
||||
@@ -506,38 +598,68 @@ Loop:
|
||||
}
|
||||
// TODO: does this get added in properly?
|
||||
limiter.ReserveN(time.Now(), 1) // one event
|
||||
|
||||
// this pause/resume block is the same as the upper main one
|
||||
case _, ok := <-obj.state[vertex].pauseSignal:
|
||||
if !ok {
|
||||
obj.state[vertex].pauseSignal = nil
|
||||
break RetryWait
|
||||
}
|
||||
select {
|
||||
case _, ok := <-obj.state[vertex].resumeSignal: // channel closes
|
||||
if !ok {
|
||||
closed = true
|
||||
}
|
||||
// resumed!
|
||||
}
|
||||
}
|
||||
}
|
||||
timer.Stop() // it's nice to cleanup
|
||||
delay = 0 // reset
|
||||
obj.state[vertex].init.Logf("the CheckApply delay expired!")
|
||||
}
|
||||
if failed { // don't Process anymore if we've already failed...
|
||||
// don't Process anymore if we've already failed or shutdown...
|
||||
if failed || closed {
|
||||
continue Loop
|
||||
}
|
||||
|
||||
if obj.Debug {
|
||||
obj.Logf("Process(%s)", vertex)
|
||||
}
|
||||
err = obj.Process(vertex)
|
||||
if obj.Debug {
|
||||
obj.Logf("Process(%s): Return(%+v)", vertex, err)
|
||||
backPoke := false
|
||||
err = obj.Process(obj.state[vertex].doneCtx, vertex)
|
||||
if err == engine.ErrBackPoke {
|
||||
backPoke = true
|
||||
err = nil // for future code safety
|
||||
}
|
||||
if err == nil {
|
||||
if obj.Debug && backPoke {
|
||||
obj.Logf("Process(%s): BackPoke!", vertex)
|
||||
}
|
||||
if obj.Debug && !backPoke {
|
||||
obj.Logf("Process(%s): Return(%s)", vertex, engineUtil.CleanError(err))
|
||||
}
|
||||
if err == nil && !backPoke && res.MetaParams().RetryReset { // reset it on success!
|
||||
metas.CheckApplyRetry = res.MetaParams().Retry // lookup the retry value
|
||||
}
|
||||
if err == nil || backPoke {
|
||||
break RetryLoop
|
||||
}
|
||||
// we've got an error...
|
||||
delay = res.MetaParams().Delay
|
||||
|
||||
if retry < 0 { // infinite retries
|
||||
if metas.CheckApplyRetry < 0 { // infinite retries
|
||||
continue
|
||||
}
|
||||
if retry > 0 { // don't decrement past 0
|
||||
retry--
|
||||
obj.state[vertex].init.Logf("retrying CheckApply after %.4f seconds (%d left)", float64(delay)/1000, retry)
|
||||
if metas.CheckApplyRetry > 0 { // don't decrement past 0
|
||||
metas.CheckApplyRetry--
|
||||
obj.state[vertex].init.Logf(
|
||||
"retrying CheckApply after %.4f seconds (%d left)",
|
||||
float64(delay)/1000,
|
||||
metas.CheckApplyRetry,
|
||||
)
|
||||
continue
|
||||
}
|
||||
//if retry == 0 { // optional
|
||||
//if metas.CheckApplyRetry == 0 { // optional
|
||||
// err = errwrap.Wrapf(err, "permanent process error")
|
||||
//}
|
||||
|
||||
@@ -545,7 +667,7 @@ Loop:
|
||||
// this dies. If Process fails permanently, we ask it
|
||||
// to exit right here... (It happens when we loop...)
|
||||
failed = true
|
||||
close(obj.state[vertex].processDone) // causes doneChan to close
|
||||
close(obj.state[vertex].processDone) // causes doneCtx to cancel
|
||||
reterr = errwrap.Append(reterr, err) // permanent failure
|
||||
continue
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 graph
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 autoedge
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 graph
|
||||
|
||||
@@ -109,6 +121,7 @@ func (obj *wrappedGrouper) VertexMerge(v1, v2 pgraph.Vertex) (v pgraph.Vertex, e
|
||||
if err = r1.GroupRes(r2); err != nil { // GroupRes skips stupid groupings
|
||||
return // return early on error
|
||||
}
|
||||
r2.SetParent(r1) // store who my parent is
|
||||
|
||||
// merging two resources into one should yield the sum of their semas
|
||||
if semas := r2.MetaParams().Sema; len(semas) > 0 {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 autogroup
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,13 +13,26 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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.
|
||||
|
||||
// +build !root
|
||||
//go:build !root
|
||||
|
||||
package autogroup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
@@ -62,15 +75,15 @@ func (obj *NoopResTest) Init(init *engine.Init) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *NoopResTest) Close() error {
|
||||
func (obj *NoopResTest) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (obj *NoopResTest) Watch() error {
|
||||
func (obj *NoopResTest) Watch(context.Context) error {
|
||||
return nil // not needed
|
||||
}
|
||||
|
||||
func (obj *NoopResTest) CheckApply(apply bool) (checkOK bool, err error) {
|
||||
func (obj *NoopResTest) CheckApply(ctx context.Context, apply bool) (checkOK bool, err error) {
|
||||
return true, nil // state is always okay
|
||||
}
|
||||
|
||||
@@ -189,6 +202,8 @@ func (obj *testGrouper) VertexMerge(v1, v2 pgraph.Vertex) (v pgraph.Vertex, err
|
||||
if err := r1.GroupRes(r2); err != nil { // group them first
|
||||
return nil, err
|
||||
}
|
||||
r2.SetParent(r1) // store who my parent is
|
||||
|
||||
// HACK: update the name so it matches full list of self+grouped
|
||||
res := v1.(engine.GroupableRes)
|
||||
names := strings.Split(res.Name(), ",") // load in stored names
|
||||
@@ -595,7 +610,8 @@ func TestPgraphGrouping11(t *testing.T) {
|
||||
runGraphCmp(t, g1, g2)
|
||||
}
|
||||
|
||||
/* simple merge 1
|
||||
/*
|
||||
// simple merge 1
|
||||
// a1 a2 a1,a2
|
||||
// \ / >>> | (arrows point downwards)
|
||||
// b b
|
||||
@@ -621,7 +637,8 @@ func TestPgraphGrouping12(t *testing.T) {
|
||||
runGraphCmp(t, g1, g2)
|
||||
}
|
||||
|
||||
/* simple merge 2
|
||||
/*
|
||||
// simple merge 2
|
||||
// b b
|
||||
// / \ >>> | (arrows point downwards)
|
||||
// a1 a2 a1,a2
|
||||
@@ -647,7 +664,8 @@ func TestPgraphGrouping13(t *testing.T) {
|
||||
runGraphCmp(t, g1, g2)
|
||||
}
|
||||
|
||||
/* triple merge
|
||||
/*
|
||||
// triple merge
|
||||
// a1 a2 a3 a1,a2,a3
|
||||
// \ | / >>> | (arrows point downwards)
|
||||
// b b
|
||||
@@ -676,7 +694,8 @@ func TestPgraphGrouping14(t *testing.T) {
|
||||
runGraphCmp(t, g1, g2)
|
||||
}
|
||||
|
||||
/* chain merge
|
||||
/*
|
||||
// chain merge
|
||||
// a1 a1
|
||||
// / \ |
|
||||
// b1 b2 >>> b1,b2 (arrows point downwards)
|
||||
@@ -712,7 +731,8 @@ func TestPgraphGrouping15(t *testing.T) {
|
||||
runGraphCmp(t, g1, g2)
|
||||
}
|
||||
|
||||
/* re-attach 1 (outer)
|
||||
/*
|
||||
// re-attach 1 (outer)
|
||||
// technically the second possibility is valid too, depending on which order we
|
||||
// merge edges in, and if we don't filter out any unnecessary edges afterwards!
|
||||
// a1 a2 a1,a2 a1,a2
|
||||
@@ -735,20 +755,34 @@ func TestPgraphGrouping16(t *testing.T) {
|
||||
g1.AddEdge(b1, c1, e2)
|
||||
g1.AddEdge(a2, c1, e3)
|
||||
}
|
||||
g2, _ := pgraph.NewGraph("g2") // expected result
|
||||
//g2, _ := pgraph.NewGraph("g2") // expected result
|
||||
//{
|
||||
// a := NewNoopResTest("a1,a2")
|
||||
// b1 := NewNoopResTest("b1")
|
||||
// c1 := NewNoopResTest("c1")
|
||||
// e1 := NE("e1,e3")
|
||||
// e2 := NE("e2,e3") // e3 gets "merged through" to BOTH edges!
|
||||
// g2.AddEdge(a, b1, e1)
|
||||
// g2.AddEdge(b1, c1, e2)
|
||||
//}
|
||||
//runGraphCmp(t, g1, g2)
|
||||
g3, _ := pgraph.NewGraph("g3") // alternative expected result
|
||||
{
|
||||
a := NewNoopResTest("a1,a2")
|
||||
b1 := NewNoopResTest("b1")
|
||||
c1 := NewNoopResTest("c1")
|
||||
e1 := NE("e1,e3")
|
||||
e2 := NE("e2,e3") // e3 gets "merged through" to BOTH edges!
|
||||
g2.AddEdge(a, b1, e1)
|
||||
g2.AddEdge(b1, c1, e2)
|
||||
e1 := NE("e1")
|
||||
e2 := NE("e2")
|
||||
e3 := NE("e3")
|
||||
g3.AddEdge(a, b1, e1)
|
||||
g3.AddEdge(b1, c1, e2)
|
||||
g3.AddEdge(a, c1, e3)
|
||||
}
|
||||
runGraphCmp(t, g1, g2)
|
||||
runGraphCmp(t, g1, g3)
|
||||
}
|
||||
|
||||
/* re-attach 2 (inner)
|
||||
/*
|
||||
// re-attach 2 (inner)
|
||||
// a1 b2 a1
|
||||
// | / |
|
||||
// b1 / >>> b1,b2 (arrows point downwards)
|
||||
@@ -782,13 +816,15 @@ func TestPgraphGrouping17(t *testing.T) {
|
||||
runGraphCmp(t, g1, g2)
|
||||
}
|
||||
|
||||
/* re-attach 3 (double)
|
||||
/*
|
||||
// re-attach 3 (double)
|
||||
// similar to "re-attach 1", technically there is a second possibility for this
|
||||
// a2 a1 b2 a1,a2
|
||||
// \ | / |
|
||||
// \ b1 / >>> b1,b2 (arrows point downwards)
|
||||
// \ | / |
|
||||
// c1 c1
|
||||
// TODO: verify this second possibility manually
|
||||
// a2 a1 b2 a1,a2 a1,a2
|
||||
// \ | / | | \
|
||||
// \ b1 / >>> b1,b2 OR b1,b2 / (arrows point downwards)
|
||||
// \ | / | | /
|
||||
// c1 c1 c1
|
||||
*/
|
||||
func TestPgraphGrouping18(t *testing.T) {
|
||||
g1, _ := pgraph.NewGraph("g1") // original graph
|
||||
@@ -807,20 +843,34 @@ func TestPgraphGrouping18(t *testing.T) {
|
||||
g1.AddEdge(a2, c1, e3)
|
||||
g1.AddEdge(b2, c1, e4)
|
||||
}
|
||||
g2, _ := pgraph.NewGraph("g2") // expected result
|
||||
//g2, _ := pgraph.NewGraph("g2") // expected result
|
||||
//{
|
||||
// a := NewNoopResTest("a1,a2")
|
||||
// b := NewNoopResTest("b1,b2")
|
||||
// c1 := NewNoopResTest("c1")
|
||||
// e1 := NE("e1,e3")
|
||||
// e2 := NE("e2,e3,e4") // e3 gets "merged through" to BOTH edges!
|
||||
// g2.AddEdge(a, b, e1)
|
||||
// g2.AddEdge(b, c1, e2)
|
||||
//}
|
||||
//runGraphCmp(t, g1, g2)
|
||||
g3, _ := pgraph.NewGraph("g3") // alternative expected result
|
||||
{
|
||||
a := NewNoopResTest("a1,a2")
|
||||
b := NewNoopResTest("b1,b2")
|
||||
c1 := NewNoopResTest("c1")
|
||||
e1 := NE("e1,e3")
|
||||
e2 := NE("e2,e3,e4") // e3 gets "merged through" to BOTH edges!
|
||||
g2.AddEdge(a, b, e1)
|
||||
g2.AddEdge(b, c1, e2)
|
||||
e1 := NE("e1")
|
||||
e2 := NE("e2,e4")
|
||||
e3 := NE("e3")
|
||||
g3.AddEdge(a, b, e1)
|
||||
g3.AddEdge(b, c1, e2)
|
||||
g3.AddEdge(a, c1, e3)
|
||||
}
|
||||
runGraphCmp(t, g1, g2)
|
||||
runGraphCmp(t, g1, g3)
|
||||
}
|
||||
|
||||
/* connected merge 0, (no change!)
|
||||
/*
|
||||
// connected merge 0, (no change!)
|
||||
// a1 a1
|
||||
// \ >>> \ (arrows point downwards)
|
||||
// a2 a2
|
||||
@@ -843,7 +893,8 @@ func TestPgraphGroupingConnected0(t *testing.T) {
|
||||
runGraphCmp(t, g1, g2)
|
||||
}
|
||||
|
||||
/* connected merge 1, (no change!)
|
||||
/*
|
||||
// connected merge 1, (no change!)
|
||||
// a1 a1
|
||||
// \ \
|
||||
// b >>> b (arrows point downwards)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 autogroup
|
||||
|
||||
@@ -43,8 +55,24 @@ func (ag *baseGrouper) Init(g *pgraph.Graph) error {
|
||||
if ag.graph != nil {
|
||||
return fmt.Errorf("the init method has already been called")
|
||||
}
|
||||
ag.graph = g // pointer
|
||||
ag.vertices = ag.graph.VerticesSorted() // cache in deterministic order!
|
||||
ag.graph = g // pointer
|
||||
|
||||
// We sort deterministically, first by kind, and then by name. In
|
||||
// particular, longer kind chunks sort first. So http:ui:text should
|
||||
// appear before http:server and http:ui. This is a hack so that if we
|
||||
// are doing hierarchical automatic grouping, it gives the http:ui:text
|
||||
// a chance to get grouped into http:ui, before http:ui gets grouped
|
||||
// into http:server, because once that happens, http:ui:text will never
|
||||
// get grouped, and this won't work properly. This works, because when
|
||||
// we start comparing iteratively the list of resources, it does this
|
||||
// with a O(n^2) loop that compares the X and Y zero indexes first, and
|
||||
// and then continues along. If the "longer" resources appear first,
|
||||
// then they'll group together first. We should probably put this into
|
||||
// a new Grouper struct, but for now we might as well leave it here.
|
||||
//vertices := ag.graph.VerticesSorted() // formerly
|
||||
vertices := RHVSort(ag.graph.Vertices())
|
||||
|
||||
ag.vertices = vertices // cache in deterministic order!
|
||||
ag.i = 0
|
||||
ag.j = 0
|
||||
if len(ag.vertices) == 0 { // empty graph
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 autogroup
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,12 +13,26 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 autogroup
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
"github.com/purpleidea/mgmt/pgraph"
|
||||
@@ -136,3 +150,67 @@ func VertexMerge(g *pgraph.Graph, v1, v2 pgraph.Vertex, vertexMergeFn func(pgrap
|
||||
}
|
||||
return nil // success
|
||||
}
|
||||
|
||||
// RHVSlice is a linear list of vertices. It can be sorted by the Kind, taking
|
||||
// into account the hierarchy of names separated by colons. Afterwards, it uses
|
||||
// String() to avoid the non-determinism in the map type. RHV stands for Reverse
|
||||
// Hierarchical Vertex, meaning the hierarchical topology of the vertex
|
||||
// (resource) names are used.
|
||||
type RHVSlice []pgraph.Vertex
|
||||
|
||||
// Len returns the length of the slice of vertices.
|
||||
func (obj RHVSlice) Len() int { return len(obj) }
|
||||
|
||||
// Swap swaps two elements in the slice.
|
||||
func (obj RHVSlice) Swap(i, j int) { obj[i], obj[j] = obj[j], obj[i] }
|
||||
|
||||
// Less returns the smaller element in the sort order according to the
|
||||
// aforementioned rules.
|
||||
// XXX: Add some tests to make sure I didn't get any "reverse" part backwards.
|
||||
func (obj RHVSlice) Less(i, j int) bool {
|
||||
resi, oki := obj[i].(engine.Res)
|
||||
resj, okj := obj[j].(engine.Res)
|
||||
if !oki || !okj || resi.Kind() == "" || resj.Kind() == "" {
|
||||
// One of these isn't a normal Res, so just compare normally.
|
||||
return obj[i].String() > obj[j].String() // reverse
|
||||
}
|
||||
|
||||
si := strings.Split(resi.Kind(), ":")
|
||||
sj := strings.Split(resj.Kind(), ":")
|
||||
// both lengths should each be at least one now
|
||||
li := len(si)
|
||||
lj := len(sj)
|
||||
|
||||
if li != lj { // eg: http:ui vs. http:ui:text
|
||||
return li > lj // reverse
|
||||
}
|
||||
|
||||
// same number of chunks
|
||||
for k := 0; k < li; k++ {
|
||||
if si[k] != sj[k] { // lhs chunk differs
|
||||
return si[k] > sj[k] // reverse
|
||||
}
|
||||
|
||||
// if the chunks are the same, we continue...
|
||||
}
|
||||
|
||||
// They must all have the same chunks, so finally we compare the names.
|
||||
return resi.Name() > resj.Name() // reverse
|
||||
}
|
||||
|
||||
// Sort is a convenience method.
|
||||
func (obj RHVSlice) Sort() { sort.Sort(obj) }
|
||||
|
||||
// RHVSort returns a deterministically sorted slice of all vertices in the list.
|
||||
// The order is sorted by the Kind, taking into account the hierarchy of names
|
||||
// separated by colons. Afterwards, it uses String() to avoid the
|
||||
// non-determinism in the map type. RHV stands for Reverse Hierarchical Vertex,
|
||||
// meaning the hierarchical topology of the vertex (resource) names are used.
|
||||
func RHVSort(vertices []pgraph.Vertex) []pgraph.Vertex {
|
||||
var vs []pgraph.Vertex
|
||||
for _, v := range vertices { // copy first
|
||||
vs = append(vs, v)
|
||||
}
|
||||
sort.Sort(RHVSlice(vs)) // add determinism
|
||||
return vs
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,8 +13,23 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 graph contains the actual implementation of the resource graph engine
|
||||
// that runs the graph of resources in real-time. This package has the algorithm
|
||||
// that runs all the graph transitions.
|
||||
package graph
|
||||
|
||||
import (
|
||||
@@ -25,6 +40,7 @@ import (
|
||||
|
||||
"github.com/purpleidea/mgmt/converger"
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
"github.com/purpleidea/mgmt/engine/local"
|
||||
engineUtil "github.com/purpleidea/mgmt/engine/util"
|
||||
"github.com/purpleidea/mgmt/pgraph"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
@@ -40,16 +56,18 @@ const (
|
||||
// Engine encapsulates a generic graph and manages its operations.
|
||||
type Engine struct {
|
||||
Program string
|
||||
Version string
|
||||
Hostname string
|
||||
World engine.World
|
||||
|
||||
Converger *converger.Coordinator
|
||||
Local *local.API
|
||||
World engine.World
|
||||
|
||||
// Prefix is a unique directory prefix which can be used. It should be
|
||||
// created if needed.
|
||||
Prefix string
|
||||
Converger *converger.Coordinator
|
||||
|
||||
Debug bool
|
||||
Logf func(format string, v ...interface{})
|
||||
Prefix string
|
||||
Debug bool
|
||||
Logf func(format string, v ...interface{})
|
||||
|
||||
graph *pgraph.Graph
|
||||
nextGraph *pgraph.Graph
|
||||
@@ -57,6 +75,9 @@ type Engine struct {
|
||||
waits map[pgraph.Vertex]*sync.WaitGroup // wg for the Worker func
|
||||
wlock *sync.Mutex // lock around waits map
|
||||
|
||||
mlock *sync.Mutex // metas lock
|
||||
metas map[engine.ResPtrUID]*engine.MetaState // meta state
|
||||
|
||||
slock *sync.Mutex // semaphore lock
|
||||
semas map[string]*semaphore.Semaphore
|
||||
|
||||
@@ -93,6 +114,9 @@ func (obj *Engine) Init() error {
|
||||
obj.waits = make(map[pgraph.Vertex]*sync.WaitGroup)
|
||||
obj.wlock = &sync.Mutex{}
|
||||
|
||||
obj.mlock = &sync.Mutex{}
|
||||
obj.metas = make(map[engine.ResPtrUID]*engine.MetaState)
|
||||
|
||||
obj.slock = &sync.Mutex{}
|
||||
obj.semas = make(map[string]*semaphore.Semaphore)
|
||||
|
||||
@@ -134,7 +158,7 @@ func (obj *Engine) Validate() error {
|
||||
}
|
||||
|
||||
if err := engine.Validate(res); err != nil {
|
||||
return errwrap.Wrapf(err, "the Res did not Validate")
|
||||
return fmt.Errorf("%s did not Validate: %v", res, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -151,8 +175,21 @@ func (obj *Engine) Apply(fn func(*pgraph.Graph) error) error {
|
||||
// it errors, then the running graph wasn't changed. It is recommended that you
|
||||
// pause the engine before running this, and resume it after you're done.
|
||||
func (obj *Engine) Commit() error {
|
||||
// It would be safer to lock this, but it would be slower and mask bugs.
|
||||
//obj.mutex.Lock()
|
||||
//defer obj.mutex.Unlock()
|
||||
|
||||
// TODO: Does this hurt performance or graph changes ?
|
||||
|
||||
activeMetas := make(map[engine.ResPtrUID]struct{})
|
||||
for vertex := range obj.state {
|
||||
res, ok := vertex.(engine.Res)
|
||||
if !ok { // should not happen, previously validated
|
||||
return fmt.Errorf("not a Res")
|
||||
}
|
||||
activeMetas[engine.PtrUID(res)] = struct{}{} // add
|
||||
}
|
||||
|
||||
start := []func() error{} // functions to run after graphsync to start...
|
||||
vertexAddFn := func(vertex pgraph.Vertex) error {
|
||||
// some of these validation steps happen before this Commit step
|
||||
@@ -170,12 +207,14 @@ func (obj *Engine) Commit() error {
|
||||
return fmt.Errorf("the Res state already exists")
|
||||
}
|
||||
|
||||
activeMetas[engine.PtrUID(res)] = struct{}{} // add
|
||||
|
||||
if obj.Debug {
|
||||
obj.Logf("Validate(%s)", res)
|
||||
}
|
||||
err := engine.Validate(res)
|
||||
if obj.Debug {
|
||||
obj.Logf("Validate(%s): Return(%+v)", res, err)
|
||||
obj.Logf("Validate(%s): Return(%s)", res, engineUtil.CleanError(err))
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf(err, "the Res did not Validate")
|
||||
@@ -195,11 +234,13 @@ func (obj *Engine) Commit() error {
|
||||
Vertex: vertex,
|
||||
|
||||
Program: obj.Program,
|
||||
Version: obj.Version,
|
||||
Hostname: obj.Hostname,
|
||||
|
||||
//Converger: obj.Converger,
|
||||
Local: obj.Local,
|
||||
World: obj.World,
|
||||
Prefix: statePrefix,
|
||||
//Converger: obj.Converger,
|
||||
|
||||
Debug: obj.Debug,
|
||||
Logf: func(format string, v ...interface{}) {
|
||||
@@ -227,10 +268,18 @@ func (obj *Engine) Commit() error {
|
||||
obj.wlock.Unlock()
|
||||
}()
|
||||
|
||||
obj.Logf("Worker(%s)", v)
|
||||
if obj.Debug || true {
|
||||
obj.Logf("%s: Working...", v)
|
||||
}
|
||||
// contains the Watch and CheckApply loops
|
||||
err := obj.Worker(v)
|
||||
obj.Logf("Worker(%s): Exited(%+v)", v, err)
|
||||
if obj.Debug || true {
|
||||
if s := engineUtil.CleanError(err); err != nil {
|
||||
obj.Logf("%s: Error: %s", v, s)
|
||||
} else {
|
||||
obj.Logf("%s: Exited...", v)
|
||||
}
|
||||
}
|
||||
obj.state[v].workerErr = err // store the error
|
||||
// If the Rewatch metaparam is true, then this will get
|
||||
// restarted if we do a graph cmp swap. This is why the
|
||||
@@ -245,15 +294,21 @@ func (obj *Engine) Commit() error {
|
||||
|
||||
free := []func() error{} // functions to run after graphsync to reset...
|
||||
vertexRemoveFn := func(vertex pgraph.Vertex) error {
|
||||
res, ok := vertex.(engine.Res)
|
||||
if !ok { // should not happen, previously validated
|
||||
return fmt.Errorf("not a Res")
|
||||
}
|
||||
delete(activeMetas, engine.PtrUID(res))
|
||||
|
||||
// wait for exit before starting new graph!
|
||||
close(obj.state[vertex].removeDone) // causes doneChan to close
|
||||
obj.state[vertex].Resume() // unblock from resume
|
||||
obj.waits[vertex].Wait() // sync
|
||||
close(obj.state[vertex].removeDone) // causes doneCtx to cancel
|
||||
close(obj.state[vertex].resumeSignal) // unblock (it only closes here)
|
||||
obj.waits[vertex].Wait() // sync
|
||||
|
||||
// close the state and resource
|
||||
// FIXME: will this mess up the sync and block the engine?
|
||||
if err := obj.state[vertex].Close(); err != nil {
|
||||
return errwrap.Wrapf(err, "the Res did not Close")
|
||||
if err := obj.state[vertex].Cleanup(); err != nil {
|
||||
return errwrap.Wrapf(err, "the Res did not Cleanup")
|
||||
}
|
||||
|
||||
// delete to free up memory from old graphs
|
||||
@@ -309,6 +364,23 @@ func (obj *Engine) Commit() error {
|
||||
if err := obj.graph.GraphSync(obj.nextGraph, vertexCmpFn, vertexAddFn, vertexRemoveFn, engine.EdgeCmpFn); err != nil {
|
||||
return errwrap.Wrapf(err, "error running graph sync")
|
||||
}
|
||||
|
||||
// This happens after GraphSync when vertexRemoveFn and vertexAddFn are
|
||||
// done running. Those two modified the activeMetas map. It's important
|
||||
// that vertexRemoveFn runs before vertexAddFn, but GraphSync guarantees
|
||||
// that, and it would be kind of illogical to not run things that way.
|
||||
metaGC := make(map[engine.ResPtrUID]struct{}) // which metas should we garbage collect?
|
||||
obj.mlock.Lock()
|
||||
for ptrUID := range obj.metas {
|
||||
if _, exists := activeMetas[ptrUID]; !exists {
|
||||
metaGC[ptrUID] = struct{}{}
|
||||
}
|
||||
}
|
||||
for ptrUID := range metaGC {
|
||||
delete(obj.metas, ptrUID) // otherwise, this could grow forever
|
||||
}
|
||||
obj.mlock.Unlock()
|
||||
|
||||
// We run these afterwards, so that we don't unnecessarily start anyone
|
||||
// if GraphSync failed in some way. Otherwise we'd have to do clean up!
|
||||
for _, fn := range start {
|
||||
@@ -344,8 +416,13 @@ func (obj *Engine) Commit() error {
|
||||
// Resume runs the currently active graph. It also un-pauses the graph if it was
|
||||
// paused. Very little that is interesting should happen here. It all happens in
|
||||
// the Commit method. After Commit, new things are already started, but we still
|
||||
// need to Resume any pre-existing resources.
|
||||
// need to Resume any pre-existing resources. Do not call this concurrently with
|
||||
// the Pause method.
|
||||
func (obj *Engine) Resume() error {
|
||||
// It would be safer to lock this, but it would be slower and mask bugs.
|
||||
//obj.mutex.Lock()
|
||||
//defer obj.mutex.Unlock()
|
||||
|
||||
if !obj.paused {
|
||||
return fmt.Errorf("already resumed")
|
||||
}
|
||||
@@ -358,8 +435,22 @@ func (obj *Engine) Resume() error {
|
||||
reversed := pgraph.Reverse(topoSort)
|
||||
|
||||
for _, vertex := range reversed {
|
||||
// The very first resume is skipped as those resources are
|
||||
// already running! We could do that by checking here, but it is
|
||||
// more convenient to just have a state struct field (paused) to
|
||||
// track things for this instead. As a bonus, it helps us know
|
||||
// if a resource is paused or not if we print for debugging.
|
||||
//if !obj.state[vertex].initialStartupDone {
|
||||
// obj.state[vertex].initialStartupDone = true
|
||||
// continue
|
||||
//}
|
||||
|
||||
//obj.state[vertex].starter = (indegree[vertex] == 0)
|
||||
obj.state[vertex].Resume() // doesn't error
|
||||
// This always works because if a resource errored while it was
|
||||
// paused, then we're in the paused state and we can still exit
|
||||
// from there. If a resource errors when we're trying to Pause
|
||||
// then it will only succeed without error if the resource ACKs.
|
||||
}
|
||||
// we wait for everyone to start before exiting!
|
||||
obj.paused = false
|
||||
@@ -380,6 +471,10 @@ func (obj *Engine) SetFastPause() {
|
||||
|
||||
// Pause the active, running graph.
|
||||
func (obj *Engine) Pause(fastPause bool) error {
|
||||
// It would be safer to lock this, but it would be slower and mask bugs.
|
||||
//obj.mutex.Lock()
|
||||
//defer obj.mutex.Unlock()
|
||||
|
||||
if obj.paused {
|
||||
return fmt.Errorf("already paused")
|
||||
}
|
||||
@@ -401,8 +496,10 @@ func (obj *Engine) Pause(fastPause bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close triggers a shutdown. Engine must be already paused before this is run.
|
||||
func (obj *Engine) Close() error {
|
||||
// Shutdown the engine. Engine must be already paused before this is run. It is
|
||||
// actually just a Load of an empty graph and a Commit. It waits for all the
|
||||
// resources to exit before returning.
|
||||
func (obj *Engine) Shutdown() error {
|
||||
emptyGraph, reterr := pgraph.NewGraph("empty")
|
||||
|
||||
// this is a graph switch (graph sync) that switches to an empty graph!
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,9 +13,21 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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.
|
||||
|
||||
// +build !root
|
||||
//go:build !root
|
||||
|
||||
package graph
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 graph
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,13 +13,24 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 graph
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
@@ -161,7 +172,7 @@ func (obj *Engine) ReversalList() (map[string]string, error) {
|
||||
result := make(map[string]string) // some key to contents
|
||||
|
||||
dir := obj.statePrefix() // loop through this dir...
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, errwrap.Wrapf(err, "error reading list of state dirs")
|
||||
} else if err != nil {
|
||||
@@ -171,7 +182,7 @@ func (obj *Engine) ReversalList() (map[string]string, error) {
|
||||
for _, x := range files {
|
||||
key := x.Name() // some uid for the resource
|
||||
file := path.Join(dir, key, ReverseFile)
|
||||
content, err := ioutil.ReadFile(file)
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, errwrap.Wrapf(err, "could not read reverse file: %s", file)
|
||||
} else if err != nil {
|
||||
@@ -231,9 +242,9 @@ func (obj *State) ReversalInit() error {
|
||||
return obj.ReversalWrite(str, res.ReversibleMeta().Overwrite) // Store!
|
||||
}
|
||||
|
||||
// ReversalClose performs the reversal shutdown steps if necessary for this
|
||||
// ReversalCleanup performs the reversal shutdown steps if necessary for this
|
||||
// resource.
|
||||
func (obj *State) ReversalClose() error {
|
||||
func (obj *State) ReversalCleanup() error {
|
||||
res, ok := obj.Vertex.(engine.ReversibleRes)
|
||||
if !ok {
|
||||
return nil // nothing to do
|
||||
@@ -246,7 +257,7 @@ func (obj *State) ReversalClose() error {
|
||||
return nil // nothing to erase, we're not a reversal resource
|
||||
}
|
||||
|
||||
if !obj.isStateOK { // did we successfully reverse?
|
||||
if !obj.isStateOK.Load() { // did we successfully reverse? (mutex RLock/RUnlock)
|
||||
obj.Logf("did not complete reversal") // warn
|
||||
return nil
|
||||
}
|
||||
@@ -263,7 +274,7 @@ func (obj *State) ReversalWrite(str string, overwrite bool) error {
|
||||
}
|
||||
file := path.Join(dir, ReverseFile) // return a unique file
|
||||
|
||||
content, err := ioutil.ReadFile(file)
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return errwrap.Wrapf(err, "could not read reverse file: %s", file)
|
||||
}
|
||||
@@ -275,12 +286,12 @@ func (obj *State) ReversalWrite(str string, overwrite bool) error {
|
||||
if str != oldStr {
|
||||
obj.Logf("existing, pending, reversible resource exists")
|
||||
//obj.Logf("diff:")
|
||||
//obj.Logf("") // TODO: print the diff w/o and secret values
|
||||
//obj.Logf("") // TODO: print the diff w/o secret values
|
||||
return fmt.Errorf("existing, pending, reversible resource exists")
|
||||
}
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(file, []byte(str), ReversePerm)
|
||||
return os.WriteFile(file, []byte(str), ReversePerm)
|
||||
}
|
||||
|
||||
// ReversalDelete removes the reversal state information for this resource.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 graph
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,9 +13,21 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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.
|
||||
|
||||
// +build !root
|
||||
//go:build !root
|
||||
|
||||
package graph
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,33 +13,114 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 graph
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
engineUtil "github.com/purpleidea/mgmt/engine/util"
|
||||
"github.com/purpleidea/mgmt/lang/types"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
)
|
||||
|
||||
// RecvFn represents a custom Recv function which can be used in place of the
|
||||
// stock, built-in one. This is needed if we want to receive from a different
|
||||
// resource data source than our own. (Only for special occasions of course!)
|
||||
type RecvFn func(engine.RecvableRes) (map[string]*engine.Send, error)
|
||||
|
||||
// SendRecv pulls in the sent values into the receive slots. It is called by the
|
||||
// receiver and must be given as input the full resource struct to receive on.
|
||||
// It applies the loaded values to the resource.
|
||||
func (obj *Engine) SendRecv(res engine.RecvableRes) (map[string]bool, error) {
|
||||
recv := res.Recv()
|
||||
if obj.Debug {
|
||||
// NOTE: this could expose private resource data like passwords
|
||||
obj.Logf("%s: SendRecv: %+v", res, recv)
|
||||
// It applies the loaded values to the resource. It is called recursively, as it
|
||||
// recurses into any grouped resources found within the first receiver. It
|
||||
// returns a map of resource pointer, to resource field key, to changed boolean.
|
||||
func SendRecv(res engine.RecvableRes, fn RecvFn) (map[engine.RecvableRes]map[string]*engine.Send, error) {
|
||||
updated := make(map[engine.RecvableRes]map[string]*engine.Send) // list of updated keys
|
||||
if groupableRes, ok := res.(engine.GroupableRes); ok {
|
||||
for _, x := range groupableRes.GetGroup() { // grouped elements
|
||||
recvableRes, ok := x.(engine.RecvableRes)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
//if obj.Debug {
|
||||
// obj.Logf("SendRecv: %s: grouped: %s", res, x) // receiving here
|
||||
//}
|
||||
// We need to recurse here so that autogrouped resources
|
||||
// inside autogrouped resources would work... In case we
|
||||
// work correctly. We just need to make sure that things
|
||||
// are grouped in the correct order, but that is not our
|
||||
// problem! Recurse and merge in the changed results...
|
||||
innerUpdated, err := SendRecv(recvableRes, fn)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf(err, "recursive SendRecv error")
|
||||
}
|
||||
for r, m := range innerUpdated { // res ptr, map
|
||||
if _, exists := updated[r]; !exists {
|
||||
updated[r] = make(map[string]*engine.Send)
|
||||
}
|
||||
for s, send := range m { // map[string]*engine.Send
|
||||
b := send.Changed
|
||||
// don't overwrite in case one exists...
|
||||
if old, exists := updated[r][s]; exists {
|
||||
b = b || old.Changed // unlikely i think
|
||||
}
|
||||
if _, exists := updated[r][s]; !exists {
|
||||
newSend := &engine.Send{
|
||||
Res: send.Res,
|
||||
Key: send.Key,
|
||||
Changed: b,
|
||||
}
|
||||
updated[r][s] = newSend
|
||||
}
|
||||
updated[r][s].Changed = b
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var updated = make(map[string]bool) // list of updated keys
|
||||
|
||||
var err error
|
||||
for k, v := range recv {
|
||||
updated[k] = false // default
|
||||
v.Changed = false // reset to the default
|
||||
recv := res.Recv()
|
||||
if fn != nil {
|
||||
recv, err = fn(res) // use a custom Recv function
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
keys := []string{}
|
||||
for k := range recv { // map[string]*Send
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
//if obj.Debug && len(keys) > 0 {
|
||||
// // NOTE: this could expose private resource data like passwords
|
||||
// obj.Logf("SendRecv: %s recv: %+v", res, strings.Join(keys, ", "))
|
||||
//}
|
||||
for k, v := range recv { // map[string]*Send
|
||||
// v.Res // SendableRes // a handle to the resource which is sending a value
|
||||
// v.Key // string // the key in the resource that we're sending
|
||||
if _, exists := updated[res]; !exists {
|
||||
updated[res] = make(map[string]*engine.Send)
|
||||
}
|
||||
|
||||
//updated[res][k] = false // default
|
||||
v.Changed = false // reset to the default
|
||||
updated[res][k] = v // default
|
||||
|
||||
var st interface{} = v.Res // old style direct send/recv
|
||||
if true { // new style send/recv API
|
||||
@@ -71,7 +152,7 @@ func (obj *Engine) SendRecv(res engine.RecvableRes) (map[string]bool, error) {
|
||||
}
|
||||
|
||||
obj1 := reflect.Indirect(reflect.ValueOf(st))
|
||||
type1 := obj1.Type()
|
||||
//type1 := obj1.Type()
|
||||
value1 := obj1.FieldByName(key1)
|
||||
kind1 := value1.Kind()
|
||||
|
||||
@@ -89,52 +170,110 @@ func (obj *Engine) SendRecv(res engine.RecvableRes) (map[string]bool, error) {
|
||||
}
|
||||
|
||||
obj2 := reflect.Indirect(reflect.ValueOf(res)) // pass in full struct
|
||||
type2 := obj2.Type()
|
||||
//type2 := obj2.Type()
|
||||
value2 := obj2.FieldByName(key2)
|
||||
kind2 := value2.Kind()
|
||||
|
||||
if obj.Debug {
|
||||
obj.Logf("Send(%s) has %v: %v", type1, kind1, value1)
|
||||
obj.Logf("Recv(%s) has %v: %v", type2, kind2, value2)
|
||||
//orig := value1
|
||||
dest := value2 // save the o.g. because we need the real dest!
|
||||
|
||||
// NOTE: Reminder: obj1 comes from st and it is the *<Res>Sends
|
||||
// struct which contains whichever fields that resource sends.
|
||||
// For example, this might be *TestSends for the Test resource.
|
||||
// The receiver is obj2 and that is actually the resource struct
|
||||
// which is a *<Res> and which gets it's fields directly set on.
|
||||
// For example, this might be *TestRes for the Test resource.
|
||||
//fmt.Printf("obj1(%T): %+v\n", obj1, obj1)
|
||||
//fmt.Printf("obj2(%T): %+v\n", obj2, obj2)
|
||||
// Lastly, remember that many of the type incompatibilities are
|
||||
// caught during type unification, and so we might have overly
|
||||
// relaxed the checks here and something could slip by. If we
|
||||
// find something, this code will need new checks added back.
|
||||
|
||||
// Here we unpack one-level, and then leave the complex stuff
|
||||
// for the Into() method below.
|
||||
// for kind1 == reflect.Interface || kind1 == reflect.Ptr // wrong
|
||||
// if kind1 == reflect.Interface || kind1 == reflect.Ptr // wrong
|
||||
// for kind1 == reflect.Interface // wrong
|
||||
if kind1 == reflect.Interface {
|
||||
value1 = value1.Elem() // un-nest one interface
|
||||
kind1 = value1.Kind()
|
||||
}
|
||||
|
||||
// i think we probably want the same kind, at least for now...
|
||||
if kind1 != kind2 {
|
||||
e := fmt.Errorf("kind mismatch between %s: %s and %s: %s", v.Res, kind1, res, kind2)
|
||||
err = errwrap.Append(err, e) // list of errors
|
||||
continue
|
||||
// This second block is identical, but it's just accidentally
|
||||
// symmetrical. The types of input structs are different shapes.
|
||||
// for kind2 == reflect.Interface || kind2 == reflect.Ptr // wrong
|
||||
// if kind2 == reflect.Interface || kind2 == reflect.Ptr // wrong
|
||||
// for kind2 == reflect.Interface // wrong
|
||||
if kind2 == reflect.Interface {
|
||||
value2 = value2.Elem() // un-nest one interface
|
||||
kind2 = value2.Kind()
|
||||
}
|
||||
|
||||
// if the types don't match, we can't use send->recv
|
||||
// FIXME: do we want to relax this for string -> *string ?
|
||||
if e := TypeCmp(value1, value2); e != nil {
|
||||
e := errwrap.Wrapf(e, "type mismatch between %s and %s", v.Res, res)
|
||||
err = errwrap.Append(err, e) // list of errors
|
||||
continue
|
||||
}
|
||||
//if obj.Debug {
|
||||
// obj.Logf("Send(%s) has %v: %v", type1, kind1, value1)
|
||||
// obj.Logf("Recv(%s) has %v: %v", type2, kind2, value2)
|
||||
//}
|
||||
|
||||
// Skip this check in favour of the more complex Into() below...
|
||||
//if kind1 != kind2 {
|
||||
// e := fmt.Errorf("send/recv kind mismatch between %s: %s and %s: %s", v.Res, kind1, res, kind2)
|
||||
// err = errwrap.Append(err, e) // list of errors
|
||||
// continue
|
||||
//}
|
||||
|
||||
// Skip this check in favour of the more complex Into() below...
|
||||
//if e := TypeCmp(value1, value2); e != nil {
|
||||
// e := errwrap.Wrapf(e, "type mismatch between %s and %s", v.Res, res)
|
||||
// err = errwrap.Append(err, e) // list of errors
|
||||
// continue
|
||||
//}
|
||||
|
||||
// if we can't set, then well this is pointless!
|
||||
if !value2.CanSet() {
|
||||
if !dest.CanSet() {
|
||||
e := fmt.Errorf("can't set %s.%s", res, k)
|
||||
err = errwrap.Append(err, e) // list of errors
|
||||
continue
|
||||
}
|
||||
|
||||
// if we can't interface, we can't compare...
|
||||
if !value1.CanInterface() || !value2.CanInterface() {
|
||||
if !value1.CanInterface() {
|
||||
e := fmt.Errorf("can't interface %s.%s", v.Res, v.Key)
|
||||
err = errwrap.Append(err, e) // list of errors
|
||||
continue
|
||||
}
|
||||
if !value2.CanInterface() {
|
||||
e := fmt.Errorf("can't interface %s.%s", res, k)
|
||||
err = errwrap.Append(err, e) // list of errors
|
||||
continue
|
||||
}
|
||||
|
||||
// if the values aren't equal, we're changing the receiver
|
||||
if !reflect.DeepEqual(value1.Interface(), value2.Interface()) {
|
||||
// TODO: can we catch the panics here in case they happen?
|
||||
value2.Set(value1) // do it for all types that match
|
||||
updated[k] = true // we updated this key!
|
||||
v.Changed = true // tag this key as updated!
|
||||
obj.Logf("SendRecv: %s.%s -> %s.%s", v.Res, v.Key, res, k)
|
||||
if reflect.DeepEqual(value1.Interface(), value2.Interface()) {
|
||||
continue // skip as they're the same, no error needed
|
||||
}
|
||||
|
||||
// TODO: can we catch the panics here in case they happen?
|
||||
|
||||
fv, e := types.ValueOf(value1)
|
||||
if e != nil {
|
||||
e := errwrap.Wrapf(e, "bad value %s.%s", v.Res, v.Key)
|
||||
err = errwrap.Append(err, e) // list of errors
|
||||
continue
|
||||
}
|
||||
|
||||
// mutate the struct field dest with the mcl data in fv
|
||||
if e := types.Into(fv, dest); e != nil {
|
||||
// runtime error, probably from using value res
|
||||
e := errwrap.Wrapf(e, "mismatch: %s.%s (%s) -> %s.%s (%s)", v.Res, v.Key, kind1, res, k, kind2)
|
||||
err = errwrap.Append(err, e) // list of errors
|
||||
continue
|
||||
}
|
||||
//dest.Set(orig) // do it for all types that match
|
||||
//updated[res][k] = true // we updated this key!
|
||||
v.Changed = true // tag this key as updated!
|
||||
updated[res][k] = v // we updated this key!
|
||||
//obj.Logf("SendRecv: %s.%s -> %s.%s (%+v)", v.Res, v.Key, res, k, fv) // fv may be private data
|
||||
}
|
||||
return updated, err
|
||||
}
|
||||
@@ -150,3 +289,19 @@ func TypeCmp(a, b reflect.Value) error {
|
||||
|
||||
return nil // identical Type()'s
|
||||
}
|
||||
|
||||
// UpdatedStrings returns a list of strings showing what was updated after a
|
||||
// Send/Recv run returned the updated datastructure. This is useful for logs.
|
||||
func UpdatedStrings(updated map[engine.RecvableRes]map[string]*engine.Send) []string {
|
||||
out := []string{}
|
||||
for r, m := range updated { // map[engine.RecvableRes]map[string]*engine.Send
|
||||
for s, send := range m {
|
||||
if !send.Changed {
|
||||
continue
|
||||
}
|
||||
x := fmt.Sprintf("%v.%s -> %v.%s", send.Res, send.Key, r, s)
|
||||
out = append(out, x)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,19 +13,34 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 graph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/purpleidea/mgmt/converger"
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
"github.com/purpleidea/mgmt/engine/local"
|
||||
engineUtil "github.com/purpleidea/mgmt/engine/util"
|
||||
"github.com/purpleidea/mgmt/pgraph"
|
||||
"github.com/purpleidea/mgmt/util"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
)
|
||||
|
||||
@@ -40,39 +55,51 @@ type State struct {
|
||||
Vertex pgraph.Vertex
|
||||
|
||||
Program string
|
||||
Version string
|
||||
Hostname string
|
||||
World engine.World
|
||||
|
||||
//Converger *converger.Coordinator
|
||||
|
||||
Local *local.API
|
||||
World engine.World
|
||||
|
||||
// Prefix is a unique directory prefix which can be used. It should be
|
||||
// created if needed.
|
||||
Prefix string
|
||||
|
||||
//Converger *converger.Coordinator
|
||||
|
||||
// Debug turns on additional output and behaviours.
|
||||
Debug bool
|
||||
|
||||
// Logf is the logging function that should be used to display messages.
|
||||
Logf func(format string, v ...interface{})
|
||||
|
||||
timestamp int64 // last updated timestamp
|
||||
isStateOK bool // is state OK or do we need to run CheckApply ?
|
||||
workerErr error // did the Worker error?
|
||||
timestamp int64 // last updated timestamp
|
||||
isStateOK *atomic.Bool // is state OK or do we need to run CheckApply ?
|
||||
workerErr error // did the Worker error?
|
||||
|
||||
// doneChan closes when Watch should shut down. When any of the
|
||||
mutex *sync.RWMutex // used for editing state properties
|
||||
|
||||
// doneCtx is cancelled when Watch should shut down. When any of the
|
||||
// following channels close, it causes this to close.
|
||||
doneChan chan struct{}
|
||||
doneCtx context.Context
|
||||
|
||||
// doneCtxCancel is the cancel function for doneCtx.
|
||||
doneCtxCancel func()
|
||||
|
||||
// processDone is closed when the Process/CheckApply function fails
|
||||
// permanently, and wants to cause Watch to exit.
|
||||
processDone chan struct{}
|
||||
// watchDone is closed when the Watch function fails permanently, and we
|
||||
// close this to signal we should definitely exit. (Often redundant.)
|
||||
watchDone chan struct{} // could be shared with limitDone
|
||||
watchDone chan struct{} // could be shared with limitDone or retryDone
|
||||
// limitDone is closed when the Watch function fails permanently, and we
|
||||
// close this to signal we should definitely exit. This happens inside
|
||||
// of the limit loop of the Process section of Worker.
|
||||
limitDone chan struct{} // could be shared with watchDone
|
||||
limitDone chan struct{} // could be shared with watchDone or retryDone
|
||||
// retryDone is closed when the Watch function fails permanently, and we
|
||||
// close this to signal we should definitely exit. This happens inside
|
||||
// of the retry loop of the Process section of Worker.
|
||||
retryDone chan struct{} // could be shared with watchDone or limitDone
|
||||
// removeDone is closed when the vertexRemoveFn method asks for an exit.
|
||||
// This happens when we're switching graphs. The switch to an "empty" is
|
||||
// the equivalent of asking for a final shutdown.
|
||||
@@ -94,14 +121,15 @@ type State struct {
|
||||
// to send on since it is buffered.
|
||||
pokeChan chan struct{} // outgoing from resource
|
||||
|
||||
// paused represents if this particular res is paused or not.
|
||||
// paused represents if this particular res is paused or not. This is
|
||||
// primarily used to avoid running an unnecessary Resume on the first
|
||||
// run of this resource.
|
||||
paused bool
|
||||
// pauseSignal closes to request a pause of this resource.
|
||||
// pauseSignal receives a message to request a pause of this resource.
|
||||
pauseSignal chan struct{}
|
||||
// resumeSignal closes to request a resume of this resource.
|
||||
// resumeSignal receives a message to resume this resource. The channel
|
||||
// closes when the resource is removed from the graph.
|
||||
resumeSignal chan struct{}
|
||||
// pausedAck is used to send an ack message saying that we've paused.
|
||||
pausedAck *util.EasyAck
|
||||
|
||||
wg *sync.WaitGroup // used for all vertex specific processes
|
||||
|
||||
@@ -130,11 +158,15 @@ func (obj *State) Init() error {
|
||||
return fmt.Errorf("the Logf function is missing")
|
||||
}
|
||||
|
||||
obj.doneChan = make(chan struct{})
|
||||
obj.isStateOK = &atomic.Bool{}
|
||||
|
||||
obj.mutex = &sync.RWMutex{}
|
||||
obj.doneCtx, obj.doneCtxCancel = context.WithCancel(context.Background())
|
||||
|
||||
obj.processDone = make(chan struct{})
|
||||
obj.watchDone = make(chan struct{})
|
||||
obj.limitDone = make(chan struct{})
|
||||
obj.retryDone = make(chan struct{})
|
||||
obj.removeDone = make(chan struct{})
|
||||
obj.eventsDone = make(chan struct{})
|
||||
|
||||
@@ -144,8 +176,7 @@ func (obj *State) Init() error {
|
||||
|
||||
//obj.paused = false // starts off as started
|
||||
obj.pauseSignal = make(chan struct{})
|
||||
//obj.resumeSignal = make(chan struct{}) // happens on pause
|
||||
//obj.pausedAck = util.NewEasyAck() // happens on pause
|
||||
obj.resumeSignal = make(chan struct{})
|
||||
|
||||
obj.wg = &sync.WaitGroup{}
|
||||
|
||||
@@ -154,12 +185,12 @@ func (obj *State) Init() error {
|
||||
|
||||
obj.init = &engine.Init{
|
||||
Program: obj.Program,
|
||||
Version: obj.Version,
|
||||
Hostname: obj.Hostname,
|
||||
|
||||
// Watch:
|
||||
Running: obj.event,
|
||||
Event: obj.event,
|
||||
Done: obj.doneChan,
|
||||
|
||||
// CheckApply:
|
||||
Refresh: func() bool {
|
||||
@@ -186,7 +217,7 @@ func (obj *State) Init() error {
|
||||
FilteredGraph: func() (*pgraph.Graph, error) {
|
||||
graph, err := pgraph.NewGraph("filtered")
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf(err, "could not create graph")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// filter graph and build a new one...
|
||||
@@ -228,12 +259,13 @@ func (obj *State) Init() error {
|
||||
return graph, nil // we return in a func so it's fresh!
|
||||
},
|
||||
|
||||
Local: obj.Local,
|
||||
World: obj.World,
|
||||
VarDir: obj.varDir,
|
||||
|
||||
Debug: obj.Debug,
|
||||
Logf: func(format string, v ...interface{}) {
|
||||
obj.Logf("resource: "+format, v...)
|
||||
obj.Logf(format, v...)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -249,7 +281,7 @@ func (obj *State) Init() error {
|
||||
|
||||
err := res.Init(obj.init)
|
||||
if obj.Debug {
|
||||
obj.Logf("Init(%s): Return(%+v)", res, err)
|
||||
obj.Logf("Init(%s): Return(%s)", res, engineUtil.CleanError(err))
|
||||
}
|
||||
if err != nil {
|
||||
return errwrap.Wrapf(err, "could not Init() resource")
|
||||
@@ -258,9 +290,9 @@ func (obj *State) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close shuts down and performs any cleanup. This is most akin to a "post" or
|
||||
// Cleanup shuts down and performs any cleanup. This is most akin to a "post" or
|
||||
// cleanup command as the initiator for closing a vertex happens in graph sync.
|
||||
func (obj *State) Close() error {
|
||||
func (obj *State) Cleanup() error {
|
||||
res, isRes := obj.Vertex.(engine.Res)
|
||||
if !isRes {
|
||||
return fmt.Errorf("vertex is not a Res")
|
||||
@@ -283,15 +315,15 @@ func (obj *State) Close() error {
|
||||
|
||||
var reverr error
|
||||
// clear the reverse request from the disk...
|
||||
if err := obj.ReversalClose(); err != nil {
|
||||
if err := obj.ReversalCleanup(); err != nil {
|
||||
// TODO: test this code path...
|
||||
// TODO: should this be an error or a warning?
|
||||
reverr = err
|
||||
}
|
||||
|
||||
reterr := res.Close()
|
||||
reterr := res.Cleanup()
|
||||
if obj.Debug {
|
||||
obj.Logf("Close(%s): Return(%+v)", res, reterr)
|
||||
obj.Logf("Close(%s): Return(%s)", res, engineUtil.CleanError(reterr))
|
||||
}
|
||||
|
||||
reterr = errwrap.Append(reterr, reverr)
|
||||
@@ -306,37 +338,28 @@ func (obj *State) Close() error {
|
||||
// callers are expected to make sure that they don't leave any of these running
|
||||
// by the time the Worker() shuts down.
|
||||
func (obj *State) 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...
|
||||
}
|
||||
}
|
||||
|
||||
// Pause pauses this resource. It should not be called on any already paused
|
||||
// Pause pauses this resource. It must not be called on any already paused
|
||||
// resource. It will block until the resource pauses with an acknowledgment, or
|
||||
// until an exit for that resource is seen. If the latter happens it will error.
|
||||
// It is NOT thread-safe with the Resume() method so only call either one at a
|
||||
// time.
|
||||
// It must not be called concurrently with either the Resume() method or itself,
|
||||
// so only call these one at a time and alternate between the two.
|
||||
func (obj *State) Pause() error {
|
||||
if obj.paused {
|
||||
return fmt.Errorf("already paused")
|
||||
panic("already paused")
|
||||
}
|
||||
|
||||
obj.pausedAck = util.NewEasyAck()
|
||||
obj.resumeSignal = make(chan struct{}) // build the resume signal
|
||||
close(obj.pauseSignal)
|
||||
obj.Poke() // unblock and notice the pause if necessary
|
||||
|
||||
// wait for ack (or exit signal)
|
||||
select {
|
||||
case <-obj.pausedAck.Wait(): // we got it!
|
||||
case obj.pauseSignal <- struct{}{}:
|
||||
// we're paused
|
||||
case <-obj.doneChan:
|
||||
|
||||
case <-obj.doneCtx.Done():
|
||||
return engine.ErrClosed
|
||||
}
|
||||
obj.paused = true
|
||||
@@ -344,23 +367,22 @@ func (obj *State) Pause() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Resume unpauses this resource. It can be safely called on a brand-new
|
||||
// resource that has just started running without incident. It is NOT
|
||||
// thread-safe with the Pause() method, so only call either one at a time.
|
||||
// Resume unpauses this resource. It can be safely called once on a brand-new
|
||||
// resource that has just started running, without incident. It must not be
|
||||
// called concurrently with either the Pause() method or itself, so only call
|
||||
// these one at a time and alternate between the two.
|
||||
func (obj *State) Resume() {
|
||||
// TODO: do we need a mutex around Resume?
|
||||
// This paused check prevents unnecessary "resume" calls to the resource
|
||||
// on its first run, since resources start in the running state!
|
||||
if !obj.paused { // no need to unpause brand-new resources
|
||||
return
|
||||
}
|
||||
|
||||
obj.pauseSignal = make(chan struct{}) // rebuild for next pause
|
||||
close(obj.resumeSignal)
|
||||
//obj.Poke() // not needed, we're already waiting for resume
|
||||
select {
|
||||
case obj.resumeSignal <- struct{}{}:
|
||||
}
|
||||
|
||||
obj.paused = false
|
||||
|
||||
// no need to wait for it to resume
|
||||
//return // implied
|
||||
}
|
||||
|
||||
// event is a helper function to send an event to the CheckApply process loop.
|
||||
@@ -372,7 +394,7 @@ func (obj *State) event() {
|
||||
obj.setDirty() // assume we're initially dirty
|
||||
|
||||
select {
|
||||
case obj.eventsChan <- nil:
|
||||
case obj.eventsChan <- nil: // blocks! (this is unbuffered)
|
||||
// send!
|
||||
}
|
||||
|
||||
@@ -383,11 +405,13 @@ func (obj *State) event() {
|
||||
// CheckApply will have some work to do in order to converge it.
|
||||
func (obj *State) setDirty() {
|
||||
obj.tuid.StopTimer()
|
||||
obj.isStateOK = false
|
||||
//obj.mutex.Lock()
|
||||
obj.isStateOK.Store(false) // concurrent write
|
||||
//obj.mutex.Unlock()
|
||||
}
|
||||
|
||||
// poll is a replacement for Watch when the Poll metaparameter is used.
|
||||
func (obj *State) poll(interval uint32) error {
|
||||
func (obj *State) poll(ctx context.Context, interval uint32) error {
|
||||
// create a time.Ticker for the given interval
|
||||
ticker := time.NewTicker(time.Duration(interval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
@@ -399,7 +423,7 @@ func (obj *State) poll(interval uint32) error {
|
||||
case <-ticker.C: // received the timer event
|
||||
obj.init.Logf("polling...")
|
||||
|
||||
case <-obj.init.Done: // signal for shutdown request
|
||||
case <-ctx.Done(): // signal for shutdown request
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 graph
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
|
||||
|
||||
334
engine/local/local.go
Normal file
334
engine/local/local.go
Normal file
@@ -0,0 +1,334 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2024+ 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 local contains functions and interfaces that are shared between
|
||||
// functions and resources. It's similar to the "world" functionality, except
|
||||
// that it only involves local operations that stay within a single machine or
|
||||
// local mgmt instance.
|
||||
package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/purpleidea/mgmt/util"
|
||||
)
|
||||
|
||||
// API implements the base handle for all the methods in this package. If we
|
||||
// were going to have more than one implementation for all of these, then this
|
||||
// would be an interface instead, and different packages would implement it.
|
||||
// Since this is not the expectation for the local API, it's all self-contained.
|
||||
type API struct {
|
||||
Prefix string
|
||||
Debug bool
|
||||
Logf func(format string, v ...interface{})
|
||||
|
||||
// Each piece of the API can take a handle here.
|
||||
*Value
|
||||
}
|
||||
|
||||
// Init initializes the API before first use. It returns itself so it can be
|
||||
// chained for API aesthetical purposes.
|
||||
func (obj *API) Init() *API {
|
||||
obj.Value = &Value{}
|
||||
obj.Value.Init(&ValueInit{
|
||||
Prefix: obj.Prefix,
|
||||
Debug: obj.Debug,
|
||||
Logf: obj.Logf,
|
||||
})
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
// ValueInit are the init values that the Value API needs to work correctly.
|
||||
type ValueInit struct {
|
||||
Prefix string
|
||||
Debug bool
|
||||
Logf func(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// Value is the API for getting, setting, and watching local values.
|
||||
type Value struct {
|
||||
init *ValueInit
|
||||
mutex *sync.Mutex
|
||||
prefix string
|
||||
prefixExists bool // is it okay to use the prefix?
|
||||
values map[string]interface{}
|
||||
notify map[chan struct{}]string // one chan (unique ptr) for each watch
|
||||
skipread map[string]struct{}
|
||||
}
|
||||
|
||||
// Init runs some initialization code for the Value API.
|
||||
func (obj *Value) Init(init *ValueInit) {
|
||||
obj.init = init
|
||||
obj.mutex = &sync.Mutex{}
|
||||
obj.prefix = fmt.Sprintf("%s/", path.Join(obj.init.Prefix, "value"))
|
||||
obj.values = make(map[string]interface{})
|
||||
obj.notify = make(map[chan struct{}]string)
|
||||
obj.skipread = make(map[string]struct{})
|
||||
|
||||
// We don't need to, or want to, load any of the keys from disk
|
||||
// initially, because (1) this would consume memory for keys we never
|
||||
// use, and (2) we can load them on first read instead.
|
||||
// TODO: build in some sort of expiry system that deletes keys older
|
||||
// than X weeks to prevent infinite growth of the on-disk database.
|
||||
}
|
||||
|
||||
// ValueGet pulls a value out of a local in-memory, key-value store that is
|
||||
// backed by on-disk storage. While each value is intended to have an underlying
|
||||
// type, we use the `any` or empty `interface{}` value to represent each value
|
||||
// instead of a `types.Value` because it's more generic, and not limited to
|
||||
// being used with the language type system. If the value doesn't exist, we
|
||||
// return a nil value and no error.
|
||||
func (obj *Value) ValueGet(ctx context.Context, key string) (interface{}, error) {
|
||||
prefix, err := obj.getPrefix()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
|
||||
var val interface{}
|
||||
//var err error
|
||||
if _, skip := obj.skipread[key]; skip {
|
||||
val, err = valueRead(ctx, prefix, key) // must return val == nil if missing
|
||||
if err != nil {
|
||||
// We had an actual read issue. Report this and stop
|
||||
// because it means we might not be allowing our
|
||||
// cold-cache warming if we ignored it.
|
||||
return nil, err
|
||||
}
|
||||
// File not found errors are masked in the valueRead function
|
||||
}
|
||||
|
||||
// Anything in memory, will override whatever we might have read.
|
||||
value, exists := obj.values[key]
|
||||
if !exists {
|
||||
// disable future disk reads since the cache is now warm!
|
||||
obj.skipread[key] = struct{}{}
|
||||
return val, nil // if val is nil, we didn't find it
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// ValueSet sets a value to our in-memory, key-value store that is backed by
|
||||
// on-disk storage. If you provide a nil value, this is the equivalent of
|
||||
// removing or deleting the value.
|
||||
func (obj *Value) ValueSet(ctx context.Context, key string, value interface{}) error {
|
||||
prefix, err := obj.getPrefix()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
|
||||
// Write to state dir on disk first. If ctx cancels, we assume it's not
|
||||
// written or it doesn't matter because we're cancelling, meaning we're
|
||||
// shutting down, so our local cache can be invalidated anyways.
|
||||
|
||||
if value == nil { // remove/delete
|
||||
if err := valueRemove(ctx, prefix, key); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := valueWrite(ctx, prefix, key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if value == nil { // remove/delete
|
||||
delete(obj.values, key)
|
||||
} else {
|
||||
obj.values[key] = value // store to in-memory map
|
||||
}
|
||||
|
||||
// We still notify on remove/delete!
|
||||
for ch, k := range obj.notify { // send notifications to any watchers...
|
||||
if k != key { // there might be more than one watcher per key
|
||||
continue
|
||||
}
|
||||
select {
|
||||
case ch <- struct{}{}: // must be async and not block forever
|
||||
// send
|
||||
|
||||
// We don't ever exit here, because that would be the equivalent
|
||||
// of dropping a notification on the floor. This loop is
|
||||
// non-blocking, and so it's okay to just finish it up quickly.
|
||||
//case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValueWatch watches a value from our in-memory, key-value store that is backed
|
||||
// by on-disk storage. Conveniently, it never has to watch the on-disk storage,
|
||||
// because after the initial startup which always sends a single startup event,
|
||||
// it suffices to watch the in-memory store for events!
|
||||
func (obj *Value) ValueWatch(ctx context.Context, key string) (chan struct{}, error) {
|
||||
// No need to look at the prefix on disk, because we can do all our
|
||||
// watches from memory!
|
||||
//prefix, err := obj.getPrefix()
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
|
||||
notifyCh := make(chan struct{}, 1) // so we can async send
|
||||
obj.notify[notifyCh] = key // add (while within the mutex)
|
||||
notifyCh <- struct{}{} // startup signal, send one!
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer func() { // cleanup
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
delete(obj.notify, notifyCh) // free memory (in mutex)
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-notifyCh:
|
||||
if !ok {
|
||||
// programming error
|
||||
panic("unexpected channel closure")
|
||||
}
|
||||
// recv
|
||||
|
||||
case <-ctx.Done():
|
||||
break // we exit
|
||||
}
|
||||
|
||||
select {
|
||||
case ch <- struct{}{}:
|
||||
// send
|
||||
|
||||
case <-ctx.Done():
|
||||
break // we exit
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
// getPrefix gets the prefix dir to use, or errors if it can't make one. It
|
||||
// makes it on first use, and returns quickly from any future calls to it.
|
||||
func (obj *Value) getPrefix() (string, error) {
|
||||
// NOTE: Moving this mutex to just below the first early return, would
|
||||
// be a benign race, but as it turns out, it's possible that a compiler
|
||||
// would see this behaviour as "undefined" and things might not work as
|
||||
// intended. It could perhaps be replaced with a sync/atomic primitive
|
||||
// if we wanted better performance here.
|
||||
obj.mutex.Lock()
|
||||
defer obj.mutex.Unlock()
|
||||
|
||||
if obj.prefixExists { // former race read
|
||||
return obj.prefix, nil
|
||||
}
|
||||
|
||||
// MkdirAll instead of Mkdir because we have no idea if the parent
|
||||
// local/ directory was already made yet or not. (If at all.) If path is
|
||||
// already a directory, MkdirAll does nothing and returns nil. (Good!)
|
||||
// TODO: I hope MkdirAll is thread-safe on path creation in case another
|
||||
// future local API tries to make the base (parent) directory too!
|
||||
if err := os.MkdirAll(obj.prefix, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
obj.prefixExists = true // former race write
|
||||
|
||||
return obj.prefix, nil
|
||||
}
|
||||
|
||||
func valueRead(ctx context.Context, prefix, key string) (interface{}, error) {
|
||||
// TODO: implement ctx cancellation
|
||||
// TODO: replace with my path library
|
||||
if !strings.HasSuffix(prefix, "/") {
|
||||
return nil, fmt.Errorf("prefix is not a dir")
|
||||
}
|
||||
if !strings.HasPrefix(prefix, "/") {
|
||||
return nil, fmt.Errorf("prefix is not absolute")
|
||||
}
|
||||
p := fmt.Sprintf("%s%s", prefix, key)
|
||||
|
||||
b, err := os.ReadFile(p)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil // not found
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// file exists!
|
||||
s := string(b)
|
||||
s = strings.TrimSpace(s) // get rid of any newline
|
||||
|
||||
return util.B64ToValue(s)
|
||||
}
|
||||
|
||||
func valueWrite(ctx context.Context, prefix, key string, value interface{}) error {
|
||||
// TODO: implement ctx cancellation
|
||||
// TODO: replace with my path library
|
||||
if !strings.HasSuffix(prefix, "/") {
|
||||
return fmt.Errorf("prefix is not a dir")
|
||||
}
|
||||
if !strings.HasPrefix(prefix, "/") {
|
||||
return fmt.Errorf("prefix is not absolute")
|
||||
}
|
||||
p := fmt.Sprintf("%s%s", prefix, key)
|
||||
|
||||
s, err := util.ValueToB64(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s += "\n" // files end with a newline
|
||||
return os.WriteFile(p, []byte(s), 0600)
|
||||
}
|
||||
|
||||
func valueRemove(ctx context.Context, prefix, key string) error {
|
||||
// TODO: implement ctx cancellation
|
||||
// TODO: replace with my path library
|
||||
if !strings.HasSuffix(prefix, "/") {
|
||||
return fmt.Errorf("prefix is not a dir")
|
||||
}
|
||||
if !strings.HasPrefix(prefix, "/") {
|
||||
return fmt.Errorf("prefix is not absolute")
|
||||
}
|
||||
p := fmt.Sprintf("%s%s", prefix, key)
|
||||
|
||||
if err := os.Remove(p); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil // ignore not found errors
|
||||
}
|
||||
62
engine/local/local_test.go
Normal file
62
engine/local/local_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2024+ 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 local
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
tmpdir := fmt.Sprintf("%s/", t.TempDir()) // gets cleaned up at end, new dir for each call
|
||||
key := "test1"
|
||||
value := 42
|
||||
if err := valueWrite(context.Background(), tmpdir, key, value); err != nil {
|
||||
t.Errorf("error: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if val, err := valueRead(context.Background(), tmpdir, key); err != nil {
|
||||
t.Errorf("error: %+v", err)
|
||||
return
|
||||
} else if !reflect.DeepEqual(value, val) {
|
||||
t.Errorf("error: not equal: %+v != %+v", val, value)
|
||||
//return
|
||||
}
|
||||
|
||||
if err := valueRemove(context.Background(), tmpdir, key); err != nil {
|
||||
t.Errorf("error: %+v", err)
|
||||
//return
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
|
||||
@@ -36,8 +48,9 @@ var DefaultMetaParams = &MetaParams{
|
||||
Poll: 0, // defaults to watching for events
|
||||
Limit: rate.Inf, // defaults to no limit
|
||||
Burst: 0, // no burst needed on an infinite rate
|
||||
Reset: false,
|
||||
//Sema: []string{},
|
||||
Rewatch: true,
|
||||
Rewatch: false,
|
||||
Realize: false, // true would be more awesome, but unexpected for users
|
||||
}
|
||||
|
||||
@@ -64,9 +77,16 @@ type MetaParams struct {
|
||||
// reason to want to do something differently for the Watch errors.
|
||||
|
||||
// Retry is the number of times to retry on error. Use -1 for infinite.
|
||||
// This value is used for both Watch and CheckApply.
|
||||
Retry int16 `yaml:"retry"`
|
||||
|
||||
// Delay is the number of milliseconds to wait between retries.
|
||||
// RetryReset resets the retry count for CheckApply if it succeeds. This
|
||||
// value is currently different from the count used for Watch.
|
||||
// TODO: Consider resetting retry count for watch if it sends an event?
|
||||
RetryReset bool `yaml:"retryreset"`
|
||||
|
||||
// Delay is the number of milliseconds to wait between retries. This
|
||||
// value is used for both Watch and CheckApply.
|
||||
Delay uint64 `yaml:"delay"`
|
||||
|
||||
// Poll is the number of seconds between poll intervals. Use 0 to Watch.
|
||||
@@ -78,6 +98,15 @@ type MetaParams struct {
|
||||
// Burst is the number of events to allow in a burst.
|
||||
Burst int `yaml:"burst"`
|
||||
|
||||
// Reset causes the meta param state to reset when the resource changes.
|
||||
// What this means is if you have a resource of a specific kind and name
|
||||
// and in the subsequent graph it changes because one of its params
|
||||
// changed, normally the Retry, and other params will remember their
|
||||
// state, and you'll not reset the retry counter, however if this is
|
||||
// true, then it will get reset. Note that any normal reset mechanisms
|
||||
// built into retry are not affected by this.
|
||||
Reset bool `yaml:"reset"`
|
||||
|
||||
// Sema is a list of semaphore ids in the form `id` or `id:count`. If
|
||||
// you don't specify a count, then 1 is assumed. The sema of `foo` which
|
||||
// has a count equal to 1, is different from a sema named `foo:1` which
|
||||
@@ -90,7 +119,9 @@ type MetaParams struct {
|
||||
// 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.
|
||||
// worker failure, and only then can we rely on this metaparam. This is
|
||||
// false by default, as the frequency of graph changes makes it unlikely
|
||||
// that you wanted this enabled on most resources.
|
||||
Rewatch bool `yaml:"rewatch"`
|
||||
|
||||
// Realize ensures that the resource is guaranteed to converge at least
|
||||
@@ -133,6 +164,9 @@ func (obj *MetaParams) Cmp(meta *MetaParams) error {
|
||||
if obj.Burst != meta.Burst {
|
||||
return fmt.Errorf("values for Burst are different")
|
||||
}
|
||||
if obj.Reset != meta.Reset {
|
||||
return fmt.Errorf("values for Reset are different")
|
||||
}
|
||||
|
||||
if err := util.SortedStrSliceCompare(obj.Sema, meta.Sema); err != nil {
|
||||
return errwrap.Wrapf(err, "values for Sema are different")
|
||||
@@ -180,6 +214,7 @@ func (obj *MetaParams) Copy() *MetaParams {
|
||||
Poll: obj.Poll,
|
||||
Limit: obj.Limit, // FIXME: can we copy this type like this? test me!
|
||||
Burst: obj.Burst,
|
||||
Reset: obj.Reset,
|
||||
Sema: sema,
|
||||
Rewatch: obj.Rewatch,
|
||||
Realize: obj.Realize,
|
||||
@@ -200,3 +235,18 @@ func (obj *MetaParams) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
*obj = MetaParams(raw) // restore from indirection with type conversion!
|
||||
return nil
|
||||
}
|
||||
|
||||
// MetaState is some local meta param state that is saved between resources of
|
||||
// the same kind+name unique ID. Even though the vertex/resource pointer might
|
||||
// change during a graph switch (because the params changed) we might be
|
||||
// logically referring to the same resource, and we might want to preserve some
|
||||
// data across that switch. The common example is the resource retry count. If a
|
||||
// resource failed three times, and we had a limit of five before it would be a
|
||||
// permanent failure, then we don't want to reset this counter just because we
|
||||
// changed a parameter (field) of the resource. This doesn't mean we don't want
|
||||
// to ever reset these counts. For that, flip on the reset meta param.
|
||||
type MetaState struct {
|
||||
|
||||
// CheckApplyRetry is the current retry count for CheckApply.
|
||||
CheckApplyRetry int16
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,9 +13,21 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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.
|
||||
|
||||
// +build !root
|
||||
//go:build !root
|
||||
|
||||
package engine
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,14 +13,28 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 (
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
|
||||
"github.com/purpleidea/mgmt/engine/local"
|
||||
"github.com/purpleidea/mgmt/pgraph"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
|
||||
@@ -87,6 +101,9 @@ type Init struct {
|
||||
// Program is the name of the program.
|
||||
Program string
|
||||
|
||||
// Version is the version of the program.
|
||||
Version string
|
||||
|
||||
// Hostname is the uuid for the host.
|
||||
Hostname string
|
||||
|
||||
@@ -98,10 +115,6 @@ type Init struct {
|
||||
// Event sends an event notifying the engine of a possible state change.
|
||||
Event func()
|
||||
|
||||
// Done returns a channel that will close to signal to us that it's time
|
||||
// for us to shutdown.
|
||||
Done chan struct{}
|
||||
|
||||
// Called from within CheckApply:
|
||||
|
||||
// Refresh returns whether the resource received a notification. This
|
||||
@@ -135,6 +148,11 @@ type Init struct {
|
||||
|
||||
// TODO: GraphQuery offers an interface to query the resource graph.
|
||||
|
||||
// Local has a bunch of methods and properties which are useful for
|
||||
// operations on the local machine and for communication between
|
||||
// functions and resources.
|
||||
Local *local.API
|
||||
|
||||
// World provides a connection to the outside world. This is most often
|
||||
// used for communicating with the distributed database.
|
||||
World World
|
||||
@@ -190,17 +208,21 @@ type Res interface {
|
||||
// and data from the engine.
|
||||
Init(*Init) error
|
||||
|
||||
// Close is run by the engine to clean up after the resource is done.
|
||||
Close() error
|
||||
// Cleanup is run by the engine to clean up after the resource is done.
|
||||
Cleanup() error
|
||||
|
||||
// Watch is run by the engine to monitor for state changes. If it
|
||||
// detects any, it notifies the engine which will usually run CheckApply
|
||||
// in response.
|
||||
Watch() error
|
||||
// in response. If the input context cancels, we must shutdown.
|
||||
Watch(context.Context) error
|
||||
|
||||
// CheckApply determines if the state of the resource is correct and if
|
||||
// asked to with the `apply` variable, applies the requested state.
|
||||
CheckApply(apply bool) (checkOK bool, err error)
|
||||
// asked to with the `apply` variable, applies the requested state. If
|
||||
// the input context cancels, we must return as quickly as possible. We
|
||||
// should never exit immediately if this would cause permanent
|
||||
// corruption of some sort. However it doesn't mean that a resource was
|
||||
// taken to the desired state.
|
||||
CheckApply(ctx context.Context, apply bool) (checkOK bool, err error)
|
||||
|
||||
// Cmp compares itself to another resource and returns an error if they
|
||||
// are not equivalent. This is more strict than the Adapts method of the
|
||||
@@ -220,6 +242,22 @@ func Stringer(res Res) string {
|
||||
return Repr(res.Kind(), res.Name())
|
||||
}
|
||||
|
||||
// ResPtrUID is a unique identifier that is consistent for the kind and name of
|
||||
// the resource only. This was formerly a string, but a struct is more precise.
|
||||
// The result is suitable as a unique map key.
|
||||
type ResPtrUID struct {
|
||||
kind string
|
||||
name string
|
||||
}
|
||||
|
||||
// PtrUID generates a ResPtrUID from a resource. The result is suitable as a
|
||||
// unique map key.
|
||||
func PtrUID(res Res) ResPtrUID {
|
||||
// the use of "repr" is kind of arbitrary as long as it's unique
|
||||
//return ResPtrUID(Repr(res.Kind(), res.Name()))
|
||||
return ResPtrUID{kind: res.Kind(), name: res.Name()}
|
||||
}
|
||||
|
||||
// Validate validates a resource by checking multiple aspects. This is the main
|
||||
// entry point for running all the validation steps on a resource.
|
||||
func Validate(res Res) error {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,24 +13,35 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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.
|
||||
|
||||
// +build !noaugeas
|
||||
//go:build !noaugeas
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
"github.com/purpleidea/mgmt/engine/traits"
|
||||
"github.com/purpleidea/mgmt/recwatch"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
"github.com/purpleidea/mgmt/util/recwatch"
|
||||
|
||||
// FIXME: we vendor go/augeas because master requires augeas 1.6.0
|
||||
// and libaugeas-dev-1.6.0 is not yet available in a PPA.
|
||||
"honnef.co/go/augeas"
|
||||
)
|
||||
|
||||
@@ -51,24 +62,24 @@ type AugeasRes struct {
|
||||
init *engine.Init
|
||||
|
||||
// File is the path to the file targeted by this resource.
|
||||
File string `yaml:"file"`
|
||||
File string `lang:"file" yaml:"file"`
|
||||
|
||||
// Lens is the lens used by this resource. If specified, mgmt
|
||||
// will lower the augeas overhead by only loading that lens.
|
||||
Lens string `yaml:"lens"`
|
||||
Lens string `lang:"lens" yaml:"lens"`
|
||||
|
||||
// Sets is a list of changes that will be applied to the file, in the form of
|
||||
// ["path", "value"]. mgmt will run augeas.Get() before augeas.Set(), to
|
||||
// prevent changing the file when it is not needed.
|
||||
Sets []*AugeasSet `yaml:"sets"`
|
||||
// Sets is a list of changes that will be applied to the file, in the
|
||||
// form of ["path", "value"]. mgmt will run augeas.Get() before
|
||||
// augeas.Set(), to prevent changing the file when it is not needed.
|
||||
Sets []*AugeasSet `lang:"sets" yaml:"sets"`
|
||||
|
||||
recWatcher *recwatch.RecWatcher // used to watch the changed files
|
||||
}
|
||||
|
||||
// AugeasSet represents a key/value pair of settings to be applied.
|
||||
type AugeasSet struct {
|
||||
Path string `yaml:"path"` // The relative path to the value to be changed.
|
||||
Value string `yaml:"value"` // The value to be set on the given Path.
|
||||
Path string `lang:"path" yaml:"path"` // The relative path to the value to be changed.
|
||||
Value string `lang:"value" yaml:"value"` // The value to be set on the given Path.
|
||||
}
|
||||
|
||||
// Cmp compares this set with another one.
|
||||
@@ -119,15 +130,15 @@ func (obj *AugeasRes) Init(init *engine.Init) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close is run by the engine to clean up after the resource is done.
|
||||
func (obj *AugeasRes) Close() error {
|
||||
// Cleanup is run by the engine to clean up after the resource is done.
|
||||
func (obj *AugeasRes) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Watch is the primary listener for this resource and it outputs events. This
|
||||
// was taken from the File resource.
|
||||
// FIXME: DRY - This is taken from the file resource
|
||||
func (obj *AugeasRes) Watch() error {
|
||||
func (obj *AugeasRes) Watch(ctx context.Context) error {
|
||||
var err error
|
||||
obj.recWatcher, err = recwatch.NewRecWatcher(obj.File, false)
|
||||
if err != nil {
|
||||
@@ -156,7 +167,7 @@ func (obj *AugeasRes) Watch() error {
|
||||
}
|
||||
send = true
|
||||
|
||||
case <-obj.init.Done: // closed by the engine to signal shutdown
|
||||
case <-ctx.Done(): // closed by the engine to signal shutdown
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -169,7 +180,7 @@ func (obj *AugeasRes) Watch() error {
|
||||
}
|
||||
|
||||
// checkApplySet runs CheckApply for one element of the AugeasRes.Set
|
||||
func (obj *AugeasRes) checkApplySet(apply bool, ag *augeas.Augeas, set *AugeasSet) (bool, error) {
|
||||
func (obj *AugeasRes) checkApplySet(ctx context.Context, apply bool, ag *augeas.Augeas, set *AugeasSet) (bool, error) {
|
||||
fullpath := fmt.Sprintf("/files/%v/%v", obj.File, set.Path)
|
||||
|
||||
// We do not check for errors because errors are also thrown when
|
||||
@@ -194,7 +205,7 @@ func (obj *AugeasRes) checkApplySet(apply bool, ag *augeas.Augeas, set *AugeasSe
|
||||
}
|
||||
|
||||
// CheckApply method for Augeas resource.
|
||||
func (obj *AugeasRes) CheckApply(apply bool) (bool, error) {
|
||||
func (obj *AugeasRes) CheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
obj.init.Logf("CheckApply: %s", obj.File)
|
||||
// By default we do not set any option to augeas, we use the defaults.
|
||||
opts := augeas.None
|
||||
@@ -231,7 +242,7 @@ func (obj *AugeasRes) CheckApply(apply bool) (bool, error) {
|
||||
|
||||
checkOK := true
|
||||
for _, set := range obj.Sets {
|
||||
if setCheckOK, err := obj.checkApplySet(apply, &ag, set); err != nil {
|
||||
if setCheckOK, err := obj.checkApplySet(ctx, apply, &ag, set); err != nil {
|
||||
return false, errwrap.Wrapf(err, "augeas: error during CheckApply of one Set")
|
||||
} else if !setCheckOK {
|
||||
checkOK = false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 resources
|
||||
|
||||
@@ -24,7 +36,7 @@ import (
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
@@ -147,26 +159,42 @@ var AwsRegions = []string{
|
||||
// http://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html
|
||||
type AwsEc2Res struct {
|
||||
traits.Base // add the base methods without re-implementation
|
||||
traits.Sendable
|
||||
|
||||
init *engine.Init
|
||||
|
||||
State string `yaml:"state"` // state: running, stopped, terminated
|
||||
Region string `yaml:"region"` // region must match an element of AwsRegions
|
||||
Type string `yaml:"type"` // type of ec2 instance, eg: t2.micro
|
||||
ImageID string `yaml:"imageid"` // imageid must be available on the chosen region
|
||||
// State must be running, stopped, or terminated.
|
||||
State string `lang:"state" yaml:"state"`
|
||||
|
||||
// Region must match one of the AwsRegions. This list is static at the
|
||||
// moment.
|
||||
Region string `lang:"region" yaml:"region"`
|
||||
|
||||
// Type of ec2 instance, eg: t2.micro for example.
|
||||
Type string `lang:"type" yaml:"type"`
|
||||
|
||||
// ImageID to use, and note that it must be available on the chosen
|
||||
// region.
|
||||
ImageID string `lang:"imageid" yaml:"imageid"`
|
||||
|
||||
// WatchEndpoint is the public url of the sns endpoint, eg:
|
||||
// http://server:12345/ for example.
|
||||
WatchEndpoint string `lang:"watchendpoint" yaml:"watchendpoint"`
|
||||
|
||||
// WatchListenAddr is the local address or port that the sns listens on,
|
||||
// eg: 10.0.0.0:23456 or 23456.
|
||||
WatchListenAddr string `lang:"watchlistenaddr" yaml:"watchlistenaddr"`
|
||||
|
||||
WatchEndpoint string `yaml:"watchendpoint"` // the public url of the sns endpoint, eg: http://server:12345/
|
||||
WatchListenAddr string `yaml:"watchlistenaddr"` // the local address or port that the sns listens on, eg: 10.0.0.0:23456 or 23456
|
||||
// ErrorOnMalformedPost controls whether or not malformed HTTP post
|
||||
// requests, that cause JSON decoder errors, will also make the engine
|
||||
// shut down. If ErrorOnMalformedPost set to true and an error occurs,
|
||||
// Watch() will return the error and the engine will shut down.
|
||||
ErrorOnMalformedPost bool `yaml:"erroronmalformedpost"`
|
||||
ErrorOnMalformedPost bool `lang:"erroronmalformedpost" yaml:"erroronmalformedpost"`
|
||||
|
||||
// UserData is used to run bash and cloud-init commands on first launch.
|
||||
// See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html
|
||||
// for documantation and examples.
|
||||
UserData string `yaml:"userdata"`
|
||||
UserData string `lang:"userdata" yaml:"userdata"`
|
||||
|
||||
client *ec2.EC2 // client session for AWS API calls
|
||||
|
||||
@@ -388,10 +416,11 @@ func (obj *AwsEc2Res) Init(init *engine.Init) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close cleans up when we're done. This is needed to delete some of the AWS
|
||||
// Cleanup cleans up when we're done. This is needed to delete some of the AWS
|
||||
// objects created for the SNS endpoint.
|
||||
func (obj *AwsEc2Res) Close() error {
|
||||
func (obj *AwsEc2Res) Cleanup() error {
|
||||
var errList error
|
||||
// XXX: do these in a defer of Watch instead.
|
||||
// clean up sns objects created by Init/snsWatch
|
||||
if obj.snsClient != nil {
|
||||
// delete the topic and associated subscriptions
|
||||
@@ -409,16 +438,16 @@ func (obj *AwsEc2Res) Close() error {
|
||||
}
|
||||
|
||||
// Watch is the primary listener for this resource and it outputs events.
|
||||
func (obj *AwsEc2Res) Watch() error {
|
||||
func (obj *AwsEc2Res) Watch(ctx context.Context) error {
|
||||
if obj.WatchListenAddr != "" {
|
||||
return obj.snsWatch()
|
||||
return obj.snsWatch(ctx)
|
||||
}
|
||||
return obj.longpollWatch()
|
||||
return obj.longpollWatch(ctx)
|
||||
}
|
||||
|
||||
// longpollWatch uses the ec2 api's built in methods to watch ec2 resource
|
||||
// state.
|
||||
func (obj *AwsEc2Res) longpollWatch() error {
|
||||
func (obj *AwsEc2Res) longpollWatch(ctx context.Context) error {
|
||||
send := false
|
||||
|
||||
// We tell the engine that we're running right away. This is not correct,
|
||||
@@ -426,7 +455,7 @@ func (obj *AwsEc2Res) longpollWatch() error {
|
||||
obj.init.Running() // when started, notify engine that we're running
|
||||
|
||||
// cancellable context used for exiting cleanly
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
innerCtx, cancel := context.WithCancel(context.TODO())
|
||||
|
||||
// clean up when we're done
|
||||
defer obj.wg.Wait()
|
||||
@@ -461,7 +490,7 @@ func (obj *AwsEc2Res) longpollWatch() error {
|
||||
}
|
||||
|
||||
// wait for the instance state to change
|
||||
state, err := stateWaiter(ctx, instance, obj.client)
|
||||
state, err := stateWaiter(innerCtx, instance, obj.client)
|
||||
if err != nil {
|
||||
select {
|
||||
case obj.awsChan <- &chanStruct{
|
||||
@@ -502,7 +531,7 @@ func (obj *AwsEc2Res) longpollWatch() error {
|
||||
send = true
|
||||
}
|
||||
|
||||
case <-obj.init.Done: // closed by the engine to signal shutdown
|
||||
case <-ctx.Done(): // closed by the engine to signal shutdown
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -518,7 +547,7 @@ func (obj *AwsEc2Res) longpollWatch() error {
|
||||
// Init() a CloudWatch rule is created along with a corresponding SNS topic that
|
||||
// it can publish to. snsWatch creates an http server which listens for messages
|
||||
// published to the topic and processes them accordingly.
|
||||
func (obj *AwsEc2Res) snsWatch() error {
|
||||
func (obj *AwsEc2Res) snsWatch(ctx context.Context) error {
|
||||
send := false
|
||||
defer obj.wg.Wait()
|
||||
// create the sns listener
|
||||
@@ -533,9 +562,9 @@ func (obj *AwsEc2Res) snsWatch() error {
|
||||
}
|
||||
// close the listener and shutdown the sns server when we're done
|
||||
defer func() {
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), SnsServerShutdownTimeout*time.Second)
|
||||
innerCtx, cancel := context.WithTimeout(context.TODO(), SnsServerShutdownTimeout*time.Second)
|
||||
defer cancel()
|
||||
if err := snsServer.Shutdown(ctx); err != nil {
|
||||
if err := snsServer.Shutdown(innerCtx); err != nil {
|
||||
if err != context.Canceled {
|
||||
obj.init.Logf("error stopping sns endpoint: %s", err)
|
||||
return
|
||||
@@ -596,7 +625,7 @@ func (obj *AwsEc2Res) snsWatch() error {
|
||||
obj.init.Logf("State: %v", msg.event)
|
||||
send = true
|
||||
|
||||
case <-obj.init.Done: // closed by the engine to signal shutdown
|
||||
case <-ctx.Done(): // closed by the engine to signal shutdown
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -608,7 +637,7 @@ func (obj *AwsEc2Res) snsWatch() error {
|
||||
}
|
||||
|
||||
// CheckApply method for AwsEc2 resource.
|
||||
func (obj *AwsEc2Res) CheckApply(apply bool) (bool, error) {
|
||||
func (obj *AwsEc2Res) CheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
obj.init.Logf("CheckApply(%t)", apply)
|
||||
|
||||
// find the instance we need to check
|
||||
@@ -718,7 +747,7 @@ func (obj *AwsEc2Res) CheckApply(apply bool) (bool, error) {
|
||||
}
|
||||
|
||||
// context to cancel the waiter if it takes too long
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), waitTimeout*time.Second)
|
||||
innerCtx, cancel := context.WithTimeout(ctx, waitTimeout*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// wait until the state converges
|
||||
@@ -727,11 +756,11 @@ func (obj *AwsEc2Res) CheckApply(apply bool) (bool, error) {
|
||||
}
|
||||
switch obj.State {
|
||||
case ec2.InstanceStateNameRunning:
|
||||
err = obj.client.WaitUntilInstanceRunningWithContext(ctx, waitInput)
|
||||
err = obj.client.WaitUntilInstanceRunningWithContext(innerCtx, waitInput)
|
||||
case ec2.InstanceStateNameStopped:
|
||||
err = obj.client.WaitUntilInstanceStoppedWithContext(ctx, waitInput)
|
||||
err = obj.client.WaitUntilInstanceStoppedWithContext(innerCtx, waitInput)
|
||||
case ec2.InstanceStateNameTerminated:
|
||||
err = obj.client.WaitUntilInstanceTerminatedWithContext(ctx, waitInput)
|
||||
err = obj.client.WaitUntilInstanceTerminatedWithContext(innerCtx, waitInput)
|
||||
default:
|
||||
return false, errwrap.Wrapf(err, "unrecognized instance state")
|
||||
}
|
||||
@@ -963,7 +992,7 @@ func (obj *AwsEc2Res) snsGetCert(url string) (*x509.Certificate, error) {
|
||||
return nil, errwrap.Wrapf(err, "http get error")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf(err, "error reading post body")
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 resources
|
||||
|
||||
@@ -57,7 +69,6 @@ type ConfigEtcdRes struct {
|
||||
sizeFlag bool
|
||||
|
||||
interruptChan chan struct{}
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
// Default returns some sensible defaults for this resource.
|
||||
@@ -83,26 +94,22 @@ func (obj *ConfigEtcdRes) Init(init *engine.Init) error {
|
||||
obj.init = init // save for later
|
||||
|
||||
obj.interruptChan = make(chan struct{})
|
||||
obj.wg = &sync.WaitGroup{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close is run by the engine to clean up after the resource is done.
|
||||
func (obj *ConfigEtcdRes) Close() error {
|
||||
obj.wg.Wait() // bonus
|
||||
// Cleanup is run by the engine to clean up after the resource is done.
|
||||
func (obj *ConfigEtcdRes) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Watch is the primary listener for this resource and it outputs events.
|
||||
func (obj *ConfigEtcdRes) Watch() error {
|
||||
obj.wg.Add(1)
|
||||
defer obj.wg.Done()
|
||||
// FIXME: add timeout to context
|
||||
// The obj.init.Done channel is closed by the engine to signal shutdown.
|
||||
ctx, cancel := util.ContextWithCloser(context.Background(), obj.init.Done)
|
||||
func (obj *ConfigEtcdRes) Watch(ctx context.Context) error {
|
||||
wg := &sync.WaitGroup{}
|
||||
defer wg.Wait()
|
||||
innerCtx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
ch, err := obj.init.World.IdealClusterSizeWatch(util.CtxWithWg(ctx, obj.wg))
|
||||
ch, err := obj.init.World.IdealClusterSizeWatch(util.CtxWithWg(innerCtx, wg))
|
||||
if err != nil {
|
||||
return errwrap.Wrapf(err, "could not watch ideal cluster size")
|
||||
}
|
||||
@@ -121,7 +128,8 @@ Loop:
|
||||
}
|
||||
// pass through and send an event
|
||||
|
||||
case <-obj.init.Done: // closed by the engine to signal shutdown
|
||||
case <-ctx.Done(): // closed by the engine to signal shutdown
|
||||
return nil
|
||||
}
|
||||
|
||||
obj.init.Event() // notify engine of an event (this can block)
|
||||
@@ -134,10 +142,10 @@ Loop:
|
||||
// to zero, then it *won't* try and change it away from zero, because it assumes
|
||||
// that someone has requested a shutdown. If the value is seen on first startup,
|
||||
// then it will change it, because it might be a zero from the previous cluster.
|
||||
func (obj *ConfigEtcdRes) sizeCheckApply(apply bool) (bool, error) {
|
||||
func (obj *ConfigEtcdRes) sizeCheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
wg := &sync.WaitGroup{}
|
||||
defer wg.Wait() // this must be above the defer cancel() call
|
||||
ctx, cancel := context.WithTimeout(context.Background(), sizeCheckApplyTimeout)
|
||||
ctx, cancel := context.WithTimeout(ctx, sizeCheckApplyTimeout)
|
||||
defer cancel()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
@@ -186,17 +194,17 @@ func (obj *ConfigEtcdRes) sizeCheckApply(apply bool) (bool, error) {
|
||||
}
|
||||
|
||||
// CheckApply method for Noop resource. Does nothing, returns happy!
|
||||
func (obj *ConfigEtcdRes) CheckApply(apply bool) (bool, error) {
|
||||
func (obj *ConfigEtcdRes) CheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
checkOK := true
|
||||
|
||||
if c, err := obj.sizeCheckApply(apply); err != nil {
|
||||
if c, err := obj.sizeCheckApply(ctx, apply); err != nil {
|
||||
return false, err
|
||||
} else if !c {
|
||||
checkOK = false
|
||||
}
|
||||
|
||||
// TODO: add more config settings management here...
|
||||
//if c, err := obj.TODOCheckApply(apply); err != nil {
|
||||
//if c, err := obj.TODOCheckApply(ctx, apply); err != nil {
|
||||
// return false, err
|
||||
//} else if !c {
|
||||
// checkOK = false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 resources
|
||||
|
||||
@@ -108,8 +120,8 @@ func (obj *ConsulKVRes) Init(init *engine.Init) error {
|
||||
return errwrap.Wrapf(err, "could not create Consul client")
|
||||
}
|
||||
|
||||
// Close is run by the engine to clean up after the resource is done.
|
||||
func (obj *ConsulKVRes) Close() error {
|
||||
// Cleanup is run by the engine to clean up after the resource is done.
|
||||
func (obj *ConsulKVRes) Cleanup() error {
|
||||
if obj.config != nil && obj.config.Transport != nil {
|
||||
obj.config.Transport.CloseIdleConnections()
|
||||
}
|
||||
@@ -117,7 +129,7 @@ func (obj *ConsulKVRes) Close() error {
|
||||
}
|
||||
|
||||
// Watch is the listener and main loop for this resource and it outputs events.
|
||||
func (obj *ConsulKVRes) Watch() error {
|
||||
func (obj *ConsulKVRes) Watch(ctx context.Context) error {
|
||||
wg := &sync.WaitGroup{}
|
||||
defer wg.Wait()
|
||||
|
||||
@@ -132,9 +144,9 @@ func (obj *ConsulKVRes) Watch() error {
|
||||
defer wg.Done()
|
||||
|
||||
opts := &api.QueryOptions{RequireConsistent: true}
|
||||
ctx, cancel := util.ContextWithCloser(context.Background(), exit)
|
||||
innerCtx, cancel := util.ContextWithCloser(context.Background(), exit)
|
||||
defer cancel()
|
||||
opts = opts.WithContext(ctx)
|
||||
opts = opts.WithContext(innerCtx)
|
||||
|
||||
for {
|
||||
_, meta, err := kv.Get(obj.key, opts)
|
||||
@@ -162,10 +174,10 @@ func (obj *ConsulKVRes) Watch() error {
|
||||
// Unexpected situation, bug in consul API...
|
||||
select {
|
||||
case ch <- fmt.Errorf("unexpected behaviour in Consul API"):
|
||||
case <-obj.init.Done: // signal for shutdown request
|
||||
case <-ctx.Done(): // signal for shutdown request
|
||||
}
|
||||
|
||||
case <-obj.init.Done: // signal for shutdown request
|
||||
case <-ctx.Done(): // signal for shutdown request
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -186,7 +198,7 @@ func (obj *ConsulKVRes) Watch() error {
|
||||
}
|
||||
obj.init.Event()
|
||||
|
||||
case <-obj.init.Done: // signal for shutdown request
|
||||
case <-ctx.Done(): // signal for shutdown request
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -195,7 +207,8 @@ func (obj *ConsulKVRes) Watch() error {
|
||||
// CheckApply is run to check the state and, if apply is true, to apply the
|
||||
// necessary changes to reach the desired state. This is run before Watch and
|
||||
// again if Watch finds a change occurring to the state.
|
||||
func (obj *ConsulKVRes) CheckApply(apply bool) (bool, error) {
|
||||
func (obj *ConsulKVRes) CheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
// XXX: use ctx for get and put
|
||||
if obj.init.Debug {
|
||||
obj.init.Logf("consul key: %s", obj.key)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 resources
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,7 +13,19 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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 resources
|
||||
|
||||
@@ -29,14 +41,14 @@ import (
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
"github.com/purpleidea/mgmt/engine/traits"
|
||||
engineUtil "github.com/purpleidea/mgmt/engine/util"
|
||||
"github.com/purpleidea/mgmt/recwatch"
|
||||
"github.com/purpleidea/mgmt/util"
|
||||
"github.com/purpleidea/mgmt/util/errwrap"
|
||||
"github.com/purpleidea/mgmt/util/recwatch"
|
||||
|
||||
sdbus "github.com/coreos/go-systemd/dbus"
|
||||
"github.com/coreos/go-systemd/unit"
|
||||
systemdUtil "github.com/coreos/go-systemd/util"
|
||||
"github.com/godbus/dbus"
|
||||
sdbus "github.com/coreos/go-systemd/v22/dbus"
|
||||
"github.com/coreos/go-systemd/v22/unit"
|
||||
systemdUtil "github.com/coreos/go-systemd/v22/util"
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -75,6 +87,7 @@ func init() {
|
||||
}
|
||||
|
||||
// CronRes is a systemd-timer cron resource.
|
||||
// TODO: If we want to have an actual `crond` resource, name it LegacyCron.
|
||||
type CronRes struct {
|
||||
traits.Base
|
||||
traits.Edgeable
|
||||
@@ -86,46 +99,52 @@ type CronRes struct {
|
||||
// Unit is the name of the systemd service unit. It is only necessary to
|
||||
// set if you want to specify a service with a different name than the
|
||||
// resource.
|
||||
Unit string `yaml:"unit"`
|
||||
Unit string `lang:"unit" yaml:"unit"`
|
||||
|
||||
// State must be 'exists' or 'absent'.
|
||||
State string `yaml:"state"`
|
||||
State string `lang:"state" yaml:"state"`
|
||||
|
||||
// Session, if true, creates the timer as the current user, rather than
|
||||
// root. The service it points to must also be a user unit. It defaults to
|
||||
// false.
|
||||
Session bool `yaml:"session"`
|
||||
// root. The service it points to must also be a user unit. It defaults
|
||||
// to false.
|
||||
Session bool `lang:"session" yaml:"session"`
|
||||
|
||||
// Trigger is the type of timer. Valid types are 'OnCalendar',
|
||||
// 'OnActiveSec'. 'OnBootSec'. 'OnStartupSec'. 'OnUnitActiveSec', and
|
||||
// 'OnUnitInactiveSec'. For more information see 'man systemd.timer'.
|
||||
Trigger string `yaml:"trigger"`
|
||||
Trigger string `lang:"trigger" yaml:"trigger"`
|
||||
|
||||
// Time must be used with all triggers. For 'OnCalendar', it must be in
|
||||
// the format defined in 'man systemd-time' under the heading 'Calendar
|
||||
// Events'. For all other triggers, time should be a valid time span as
|
||||
// defined in 'man systemd-time'
|
||||
Time string `yaml:"time"`
|
||||
Time string `lang:"time" yaml:"time"`
|
||||
|
||||
// AccuracySec is the accuracy of the timer in systemd-time time span
|
||||
// format. It defaults to one minute.
|
||||
AccuracySec string `yaml:"accuracysec"`
|
||||
AccuracySec string `lang:"accuracysec" yaml:"accuracysec"`
|
||||
|
||||
// RandomizedDelaySec delays the timer by a randomly selected, evenly
|
||||
// distributed amount of time between 0 and the specified time value. The
|
||||
// value must be a valid systemd-time time span.
|
||||
RandomizedDelaySec string `yaml:"randomizeddelaysec"`
|
||||
// distributed amount of time between 0 and the specified time value.
|
||||
// The value must be a valid systemd-time time span.
|
||||
RandomizedDelaySec string `lang:"randomizeddelaysec" yaml:"randomizeddelaysec"`
|
||||
|
||||
// Persistent, if true, means the time when the service unit was last
|
||||
// triggered is stored on disk. When the timer is activated, the service
|
||||
// unit is triggered immediately if it would have been triggered at least
|
||||
// once during the time when the timer was inactive. It defaults to false.
|
||||
Persistent bool `yaml:"persistent"`
|
||||
// unit is triggered immediately if it would have been triggered at
|
||||
// least once during the time when the timer was inactive. It defaults
|
||||
// to false.
|
||||
Persistent bool `lang:"persistent" yaml:"persistent"`
|
||||
|
||||
// WakeSystem, if true, will cause the system to resume from suspend,
|
||||
// should it be suspended and if the system supports this. It defaults to
|
||||
// false.
|
||||
WakeSystem bool `yaml:"wakesystem"`
|
||||
// RemainAfterElapse, if true, means an elapsed timer will stay loaded, and
|
||||
// its state remains queriable. If false, an elapsed timer unit that cannot
|
||||
// elapse anymore is unloaded. It defaults to true.
|
||||
RemainAfterElapse bool `yaml:"remainafterelapse"`
|
||||
// should it be suspended and if the system supports this. It defaults
|
||||
// to false.
|
||||
WakeSystem bool `lang:"wakesystem" yaml:"wakesystem"`
|
||||
|
||||
// RemainAfterElapse, if true, means an elapsed timer will stay loaded,
|
||||
// and its state remains queriable. If false, an elapsed timer unit that
|
||||
// cannot elapse anymore is unloaded. It defaults to true.
|
||||
RemainAfterElapse bool `lang:"remainafterelapse" yaml:"remainafterelapse"`
|
||||
|
||||
file *FileRes // nested file resource
|
||||
recWatcher *recwatch.RecWatcher // recwatcher for nested file
|
||||
@@ -212,16 +231,16 @@ func (obj *CronRes) Init(init *engine.Init) error {
|
||||
return obj.file.Init(init)
|
||||
}
|
||||
|
||||
// Close is run by the engine to clean up after the resource is done.
|
||||
func (obj *CronRes) Close() error {
|
||||
// Cleanup is run by the engine to clean up after the resource is done.
|
||||
func (obj *CronRes) Cleanup() error {
|
||||
if obj.file != nil {
|
||||
return obj.file.Close()
|
||||
return obj.file.Cleanup()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Watch for state changes and sends a message to the bus if there is a change.
|
||||
func (obj *CronRes) Watch() error {
|
||||
func (obj *CronRes) Watch(ctx context.Context) error {
|
||||
var bus *dbus.Conn
|
||||
var err error
|
||||
|
||||
@@ -296,7 +315,7 @@ func (obj *CronRes) Watch() error {
|
||||
}
|
||||
send = true
|
||||
|
||||
case <-obj.init.Done: // closed by the engine to signal shutdown
|
||||
case <-ctx.Done(): // closed by the engine to signal shutdown
|
||||
return nil
|
||||
}
|
||||
// do all our event sending all together to avoid duplicate msgs
|
||||
@@ -310,16 +329,16 @@ func (obj *CronRes) Watch() error {
|
||||
// CheckApply is run to check the state and, if apply is true, to apply the
|
||||
// necessary changes to reach the desired state. This is run before Watch and
|
||||
// again if Watch finds a change occurring to the state.
|
||||
func (obj *CronRes) CheckApply(apply bool) (bool, error) {
|
||||
func (obj *CronRes) CheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
checkOK := true
|
||||
// use the embedded file resource to apply the correct state
|
||||
if c, err := obj.file.CheckApply(apply); err != nil {
|
||||
if c, err := obj.file.CheckApply(ctx, apply); err != nil {
|
||||
return false, errwrap.Wrapf(err, "nested file failed")
|
||||
} else if !c {
|
||||
checkOK = false
|
||||
}
|
||||
// check timer state and apply the defined state if needed
|
||||
if c, err := obj.unitCheckApply(apply); err != nil {
|
||||
if c, err := obj.unitCheckApply(ctx, apply); err != nil {
|
||||
return false, errwrap.Wrapf(err, "unitCheckApply error")
|
||||
} else if !c {
|
||||
checkOK = false
|
||||
@@ -329,7 +348,7 @@ func (obj *CronRes) CheckApply(apply bool) (bool, error) {
|
||||
|
||||
// unitCheckApply checks the state of the systemd-timer unit and, if apply is
|
||||
// true, applies the defined state.
|
||||
func (obj *CronRes) unitCheckApply(apply bool) (bool, error) {
|
||||
func (obj *CronRes) unitCheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
var conn *sdbus.Conn
|
||||
var godbusConn *dbus.Conn
|
||||
var err error
|
||||
@@ -376,7 +395,7 @@ func (obj *CronRes) unitCheckApply(apply bool) (bool, error) {
|
||||
}
|
||||
|
||||
// context for stopping/restarting the unit
|
||||
ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout*time.Second)
|
||||
ctx, cancel := context.WithTimeout(ctx, ctxTimeout*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// godbus connection for stopping/restarting the unit
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
31
engine/resources/doc.go
Normal file
31
engine/resources/doc.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2024+ 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 resources contains the implementations of all the core resources.
|
||||
package resources
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,16 +13,28 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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.
|
||||
|
||||
// +build !nodocker
|
||||
//go:build !nodocker
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -65,22 +77,27 @@ type DockerContainerRes struct {
|
||||
traits.Edgeable
|
||||
|
||||
// State of the container must be running, stopped, or removed.
|
||||
State string `yaml:"state"`
|
||||
State string `lang:"state" yaml:"state"`
|
||||
|
||||
// Image is a docker image, or image:tag.
|
||||
Image string `yaml:"image"`
|
||||
Image string `lang:"image" yaml:"image"`
|
||||
|
||||
// Cmd is a command, or list of commands to run on the container.
|
||||
Cmd []string `yaml:"cmd"`
|
||||
Cmd []string `lang:"cmd" yaml:"cmd"`
|
||||
|
||||
// Env is a list of environment variables. E.g. ["VAR=val",].
|
||||
Env []string `yaml:"env"`
|
||||
Env []string `lang:"env" yaml:"env"`
|
||||
|
||||
// Ports is a map of port bindings. E.g. {"tcp" => {80 => 8080},}.
|
||||
Ports map[string]map[int64]int64 `yaml:"ports"`
|
||||
Ports map[string]map[int64]int64 `lang:"ports" yaml:"ports"`
|
||||
|
||||
// APIVersion allows you to override the host's default client API
|
||||
// version.
|
||||
APIVersion string `yaml:"apiversion"`
|
||||
APIVersion string `lang:"apiversion" yaml:"apiversion"`
|
||||
|
||||
// Force, if true, this will destroy and redeploy the container if the
|
||||
// image is incorrect.
|
||||
Force bool `yaml:"force"`
|
||||
Force bool `lang:"force" yaml:"force"`
|
||||
|
||||
client *client.Client // docker api client
|
||||
|
||||
@@ -164,17 +181,17 @@ func (obj *DockerContainerRes) Init(init *engine.Init) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close is run by the engine to clean up after the resource is done.
|
||||
func (obj *DockerContainerRes) Close() error {
|
||||
// Cleanup is run by the engine to clean up after the resource is done.
|
||||
func (obj *DockerContainerRes) Cleanup() error {
|
||||
return obj.client.Close() // close the docker client
|
||||
}
|
||||
|
||||
// Watch is the primary listener for this resource and it outputs events.
|
||||
func (obj *DockerContainerRes) Watch() error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func (obj *DockerContainerRes) Watch(ctx context.Context) error {
|
||||
innerCtx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
eventChan, errChan := obj.client.Events(ctx, types.EventsOptions{})
|
||||
eventChan, errChan := obj.client.Events(innerCtx, types.EventsOptions{})
|
||||
|
||||
obj.init.Running() // when started, notify engine that we're running
|
||||
|
||||
@@ -196,7 +213,7 @@ func (obj *DockerContainerRes) Watch() error {
|
||||
}
|
||||
return err
|
||||
|
||||
case <-obj.init.Done: // closed by the engine to signal shutdown
|
||||
case <-ctx.Done(): // closed by the engine to signal shutdown
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -209,11 +226,11 @@ func (obj *DockerContainerRes) Watch() error {
|
||||
}
|
||||
|
||||
// CheckApply method for Docker resource.
|
||||
func (obj *DockerContainerRes) CheckApply(apply bool) (bool, error) {
|
||||
func (obj *DockerContainerRes) CheckApply(ctx context.Context, apply bool) (bool, error) {
|
||||
var id string
|
||||
var destroy bool
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), checkApplyCtxTimeout*time.Second)
|
||||
ctx, cancel := context.WithTimeout(ctx, checkApplyCtxTimeout*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// List any container whose name matches this resource.
|
||||
@@ -282,7 +299,7 @@ func (obj *DockerContainerRes) CheckApply(apply bool) (bool, error) {
|
||||
return false, errwrap.Wrapf(err, "error pulling image")
|
||||
}
|
||||
// Wait for the image to download, EOF signals that it's done.
|
||||
if _, err := ioutil.ReadAll(p); err != nil {
|
||||
if _, err := io.ReadAll(p); err != nil {
|
||||
return false, errwrap.Wrapf(err, "error reading image pull result")
|
||||
}
|
||||
|
||||
@@ -344,9 +361,12 @@ func (obj *DockerContainerRes) containerStart(ctx context.Context, id string, op
|
||||
}
|
||||
|
||||
// containerStop stops the specified container and waits for it to stop.
|
||||
func (obj *DockerContainerRes) containerStop(ctx context.Context, id string, timeout *time.Duration) error {
|
||||
func (obj *DockerContainerRes) containerStop(ctx context.Context, id string, timeout *int) error {
|
||||
ch, errCh := obj.client.ContainerWait(ctx, id, container.WaitConditionNotRunning)
|
||||
obj.client.ContainerStop(ctx, id, timeout)
|
||||
stopOpts := container.StopOptions{
|
||||
Timeout: timeout,
|
||||
}
|
||||
obj.client.ContainerStop(ctx, id, stopOpts)
|
||||
select {
|
||||
case <-ch:
|
||||
case err := <-errCh:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,16 +13,28 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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.
|
||||
|
||||
// +build !nodocker
|
||||
//go:build !nodocker
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -37,7 +49,9 @@ var res *DockerContainerRes
|
||||
|
||||
var id string
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// XXX: re-enable once docker is not broken.
|
||||
// XXX: Error: docker_container_test.go:112: failed to stop container
|
||||
func BrokenTestDockerMain(m *testing.M) {
|
||||
var setupCode, testCode, cleanupCode int
|
||||
|
||||
if err := setup(); err != nil {
|
||||
@@ -57,7 +71,7 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(setupCode + testCode + cleanupCode)
|
||||
}
|
||||
|
||||
func Test_containerStart(t *testing.T) {
|
||||
func BrokenTestContainerStart(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -85,7 +99,7 @@ func Test_containerStart(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_containerStop(t *testing.T) {
|
||||
func BrokenTestContainerStop(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -112,7 +126,7 @@ func Test_containerStop(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_containerRemove(t *testing.T) {
|
||||
func BrokenTestContainerRemove(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -153,7 +167,7 @@ func setup() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("error pulling image: %s", err)
|
||||
}
|
||||
if _, err := ioutil.ReadAll(p); err != nil {
|
||||
if _, err := io.ReadAll(p); err != nil {
|
||||
return fmt.Errorf("error reading image pull result: %s", err)
|
||||
}
|
||||
|
||||
@@ -191,7 +205,8 @@ func cleanup() error {
|
||||
}
|
||||
|
||||
if len(l) > 0 {
|
||||
if err := res.client.ContainerStop(ctx, id, nil); err != nil {
|
||||
stopOpts := container.StopOptions{}
|
||||
if err := res.client.ContainerStop(ctx, id, stopOpts); err != nil {
|
||||
return fmt.Errorf("error stopping container: %s", err)
|
||||
}
|
||||
if err := res.client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{}); err != nil {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Mgmt
|
||||
// Copyright (C) 2013-2021+ James Shubin and the project contributors
|
||||
// Copyright (C) 2013-2024+ 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
|
||||
@@ -13,16 +13,28 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
// 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.
|
||||
|
||||
// +build !nodocker
|
||||
//go:build !nodocker
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -56,10 +68,11 @@ type DockerImageRes struct {
|
||||
traits.Edgeable
|
||||
|
||||
// State of the image must be exists or absent.
|
||||
State string `yaml:"state"`
|
||||
State string `lang:"state" yaml:"state"`
|
||||
|
||||
// APIVersion allows you to override the host's default client API
|
||||
// version.
|
||||
APIVersion string `yaml:"apiversion"`
|
||||
APIVersion string `lang:"apiversion" yaml:"apiversion"`
|
||||
|
||||
image string // full image:tag format
|
||||
client *client.Client // docker api client
|
||||
@@ -125,17 +138,17 @@ func (obj *DockerImageRes) Init(init *engine.Init) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close is run by the engine to clean up after the resource is done.
|
||||
func (obj *DockerImageRes) Close() error {
|
||||
// Cleanup is run by the engine to clean up after the resource is done.
|
||||
func (obj *DockerImageRes) Cleanup() error {
|
||||
return obj.client.Close() // close the docker client
|
||||
}
|
||||
|
||||
// Watch is the primary listener for this resource and it outputs events.
|
||||
func (obj *DockerImageRes) Watch() error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func (obj *DockerImageRes) Watch(ctx context.Context) error {
|
||||
innerCtx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
eventChan, errChan := obj.client.Events(ctx, types.EventsOptions{})
|
||||
eventChan, errChan := obj.client.Events(innerCtx, types.EventsOptions{})
|
||||
|
||||
// notify engine that we're running
|
||||
obj.init.Running()
|
||||
@@ -158,7 +171,7 @@ func (obj *DockerImageRes) Watch() error {
|
||||
}
|
||||
return err
|
||||
|
||||
case <-obj.init.Done: // closed by the engine to signal shutdown
|
||||
case <-ctx.Done(): // closed by the engine to signal shutdown
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -171,8 +184,8 @@ func (obj *DockerImageRes) Watch() error {
|
||||
}
|
||||
|
||||
// CheckApply method for Docker resource.
|
||||
func (obj *DockerImageRes) CheckApply(apply bool) (checkOK bool, err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), dockerImageCheckApplyCtxTimeout*time.Second)
|
||||
func (obj *DockerImageRes) CheckApply(ctx context.Context, apply bool) (checkOK bool, err error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, dockerImageCheckApplyCtxTimeout*time.Second)
|
||||
defer cancel()
|
||||
|
||||
s, err := obj.client.ImageList(ctx, types.ImageListOptions{
|
||||
@@ -210,7 +223,7 @@ func (obj *DockerImageRes) CheckApply(apply bool) (checkOK bool, err error) {
|
||||
return false, errwrap.Wrapf(err, "error pulling image")
|
||||
}
|
||||
// Wait for the image to download, EOF signals that it's done.
|
||||
if _, err := ioutil.ReadAll(p); err != nil {
|
||||
if _, err := io.ReadAll(p); err != nil {
|
||||
return false, errwrap.Wrapf(err, "error reading image pull result")
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user