Compare commits
1074 Commits
2.0.0-Beta
...
master
Author | SHA1 | Date |
---|---|---|
|
0183efd7a6 | |
|
89be587687 | |
|
944e9ca074 | |
|
898360115c | |
|
2ab209ac79 | |
|
ea767ec2d6 | |
|
4f0749ccbe | |
|
25f347b8e3 | |
|
69527a8509 | |
|
7f7d7ab4dc | |
|
8127e0d9aa | |
|
5ef8065704 | |
|
e9db75b884 | |
|
49972737cc | |
|
6bb3a921e3 | |
|
f419ff07f1 | |
|
172c4b9ffb | |
|
7f4259c78c | |
|
b7d4a85d8e | |
|
220305e7f8 | |
|
54579b175d | |
|
ed833023a1 | |
|
e4aeaa39ec | |
|
66e852304e | |
|
ed20511377 | |
|
5ece9cb846 | |
|
a5db094db2 | |
|
b577b03ccd | |
|
b74707b638 | |
|
420c4b0a5d | |
|
b6d18ce164 | |
|
4fcec60d82 | |
|
b970fce669 | |
|
5866d72253 | |
|
a14282a62a | |
|
ac30fbb0fc | |
|
89d7f242ff | |
|
247d2f1787 | |
|
d7890aa80f | |
|
fdcc9c6621 | |
|
6074a5dd00 | |
|
fa15304b93 | |
|
d9e095682b | |
|
55195a1093 | |
|
5f0ea899fd | |
|
bef5ca02b1 | |
|
faa6e482a5 | |
|
dcc7f60b7e | |
|
14c9808302 | |
|
8902dfbedc | |
|
aab8a5eb4c | |
|
25ff937cba | |
|
0573db7ea7 | |
|
26fa2653c2 | |
|
117afbfb15 | |
|
79cd2734ea | |
|
f8bf3dfb8d | |
|
1ff9c12c5d | |
|
839d650465 | |
|
0c4d175436 | |
|
72a4c14920 | |
|
5f21a1e660 | |
|
b17b093bea | |
|
2d50a1ac59 | |
|
ff85c86891 | |
|
5e1ec254a4 | |
|
b719682e7c | |
|
57ca03e134 | |
|
e1f0d646e8 | |
|
c8e145c085 | |
|
29e7130f4e | |
|
101557c658 | |
|
58e59cc016 | |
|
0d755fc8ee | |
|
e599c3ddaa | |
|
7f923343d7 | |
|
7234efd347 | |
|
1638d6784a | |
|
970eb38581 | |
|
7e83db582d | |
|
d9e4d8417e | |
|
41da06acf9 | |
|
ed7b5a8463 | |
|
a5b5866b87 | |
|
1165445571 | |
|
2bdec7eee9 | |
|
4873194142 | |
|
f8cc0a90a2 | |
|
597b8c9087 | |
|
e01444b71e | |
|
1a037586c2 | |
|
e7c18ba842 | |
|
804a4e96bc | |
|
5232f49739 | |
|
37fae3a457 | |
|
21054692c5 | |
|
4f91bc3761 | |
|
bf3039e902 | |
|
938544c7cc | |
|
273e1a146c | |
|
90a8990c2c | |
|
5f327fc737 | |
|
51786f3afe | |
|
4e7eb5e917 | |
|
5545dd4bf3 | |
|
3263df9e8a | |
|
87dc1c8d54 | |
|
a325a0ebc4 | |
|
09cbd37793 | |
|
d827c32b12 | |
|
50a66ff652 | |
|
fd9cb3f38a | |
|
3874a5b758 | |
|
3959491fa2 | |
|
69982ee29b | |
|
69cdfc5dd0 | |
|
420d2070c5 | |
|
698892dc88 | |
|
1d44031f59 | |
|
9c1f0c794b | |
|
1f3cf95afb | |
|
9437cf58b7 | |
|
86e2dd78c1 | |
|
691058da50 | |
|
9d09211678 | |
|
164e280392 | |
|
08c111a0aa | |
|
50c2ef5a6d | |
|
ff21351864 | |
|
67a19857eb | |
|
28bbf3c872 | |
|
0825a7714e | |
|
60e5bf647c | |
|
cd64d1a52a | |
|
bf3565e38b | |
|
7b562a8c59 | |
|
0eceb42eb8 | |
|
374c76faa6 | |
|
7423b8263c | |
|
462fd1f3f9 | |
|
33ad30870f | |
|
b8de057078 | |
|
bd08cea355 | |
|
0b239070fb | |
|
7503f0d628 | |
|
a02767d994 | |
|
9f4fe2598b | |
|
a00691fe8b | |
|
9d58d8b584 | |
|
fb46bb9334 | |
|
00f0bd7366 | |
|
9efc00c2a3 | |
|
9d2c93a1cd | |
|
544d32401e | |
|
16ea8cb728 | |
|
5e52f7f0b5 | |
|
17085b44c0 | |
|
902cfde41f | |
|
c7d079691b | |
|
fc80010c19 | |
|
b6c336d06a | |
|
e73efaa4ed | |
|
9297ded05f | |
|
616ddc30d8 | |
|
8dcb09cd67 | |
|
8b97d596e0 | |
|
12082b3182 | |
|
50bdd64781 | |
|
91cb1d0864 | |
|
3bcb00034f | |
|
39aaeadfe9 | |
|
1e07a4be59 | |
|
7e496f33f3 | |
|
92dc14c781 | |
|
719d78316d | |
|
dbf78d8eda | |
|
cc2aa27264 | |
|
720214ce79 | |
|
5e74572bf1 | |
|
f97e583b96 | |
|
98895fef9a | |
|
4a6f6a5994 | |
|
7b2161e8aa | |
|
fd40adbb1b | |
|
0560e1306d | |
|
33b38dfec8 | |
|
ae21ffb011 | |
|
c4d23a7575 | |
|
86762454a9 | |
|
41635b1c57 | |
|
d041c56f08 | |
|
a08fc34ff8 | |
|
4ddcd841a6 | |
|
32b16e1dc6 | |
|
d1c63b8454 | |
|
7e262852f5 | |
|
8aa70f674c | |
|
e6600c5f66 | |
|
6946c56782 | |
|
3c7c3ea8db | |
|
0eec8ed7f1 | |
|
38ddcb8117 | |
|
26a4e2fa69 | |
|
15f24bcc16 | |
|
1124ea8281 | |
|
9b91eb7056 | |
|
bd6c3cdc87 | |
|
effdddfa6d | |
|
87f06850ab | |
|
53c1165144 | |
|
f654eb62e3 | |
|
eb27f7118e | |
|
028d887b8a | |
|
1aba8365dc | |
|
99ea826d46 | |
|
f10f5a62d7 | |
|
b81630ac5c | |
|
ab4e25d41f | |
|
c08fe17823 | |
|
57790e510a | |
|
a8db83aceb | |
|
9175465c11 | |
|
8e87c1ee7c | |
|
197dd6065b | |
|
bfd5eb246b | |
|
0e84171713 | |
|
c82f7ac9e0 | |
|
e15d6700f0 | |
|
2d965b5ca2 | |
|
bcdb799d26 | |
|
529b910cb9 | |
|
b7bbc55481 | |
|
496ac7fd16 | |
|
a102b66dcf | |
|
bb29e4c24d | |
|
5267d5286f | |
|
7e6ac525d7 | |
|
53b91615bb | |
|
cea879c320 | |
|
77336cb170 | |
|
fc0b70ce19 | |
|
0048ab1f8e | |
|
36276bbf06 | |
|
43819fbe35 | |
|
c1467b704a | |
|
cc74f22cb5 | |
|
328bd2610d | |
|
8041e8ddc7 | |
|
1577937993 | |
|
12d2595660 | |
|
dfed4daff7 | |
|
5da7e55e9d | |
|
999d554db0 | |
|
d176b92e26 | |
|
ef88b13ce9 | |
|
f47f175c9c | |
|
3459e7ef8c | |
|
596394eb26 | |
|
e923bf2728 | |
|
e5b5a61bc3 | |
|
5cad842713 | |
|
7c32e784e6 | |
|
4e83a443fd | |
|
5479d12c4a | |
|
786bb52736 | |
|
bbc1dc0b42 | |
|
aaf6867762 | |
|
515090467e | |
|
6f24d44eca | |
|
731bdc4e32 | |
|
916d3d3ef0 | |
|
7226819296 | |
|
547a367990 | |
|
b1f08d4f4d | |
|
9fbbef44d2 | |
|
c7b7d29655 | |
|
6640951936 | |
|
4c13f7d933 | |
|
f05556a1b7 | |
|
d4d4b81aa8 | |
|
207676e823 | |
|
dc5c36abf3 | |
|
c73ad1c291 | |
|
8ee307664b | |
|
c7f896d6d8 | |
|
5778044ef1 | |
|
def75c22b3 | |
|
52ab1d592b | |
|
f9d5247901 | |
|
5df86aba05 | |
|
b85a60d2ac | |
|
da8ae75699 | |
|
5449439d8c | |
|
516a7a0541 | |
|
f86aad1328 | |
|
d30371d934 | |
|
f5f7ac8ef9 | |
|
a7df0a15e5 | |
|
5df01f1c38 | |
|
6cfff1688a | |
|
84b76bd47d | |
|
b2147c5623 | |
|
bc9af94721 | |
|
54877ef081 | |
|
a8a28724f3 | |
|
1ba88a9d4a | |
|
77124b385a | |
|
7e530592df | |
|
d65c8afa34 | |
|
78d6dec0e9 | |
|
852ae25509 | |
|
4fbd960852 | |
|
d4b5796564 | |
|
d23414ecc2 | |
|
7b4bedb944 | |
|
3e858b2768 | |
|
9e56c6cce6 | |
|
784d7b55fc | |
|
0aceec5301 | |
|
811307d298 | |
|
452d64f8c8 | |
|
27c171aca2 | |
|
cea8be3a8f | |
|
5d9a8d08e9 | |
|
39099d0415 | |
|
11cb8f3972 | |
|
f49fa05a25 | |
|
399af62ca8 | |
|
7a29dc1eb1 | |
|
7e12fc4c7f | |
|
52593fb9cf | |
|
7a57192588 | |
|
94680e81e4 | |
|
8f9febfe08 | |
|
6307a98f92 | |
|
e8258d6846 | |
|
faa3627879 | |
|
bb440940c9 | |
|
8b0fd42992 | |
|
d23bc6a01c | |
|
318994ac20 | |
|
803b4f5c1c | |
|
ad4ecca381 | |
|
9f0943d4f4 | |
|
162eba1a3d | |
|
11b8597eca | |
|
8ad33ad9d3 | |
|
944ba06e33 | |
|
fd2a99639d | |
|
3349d1d54a | |
|
96590d2cac | |
|
eabfc5e372 | |
|
ee5a04644f | |
|
0390cdff2c | |
|
2c5557a337 | |
|
2acc7835f3 | |
|
0bd912899b | |
|
7f2d8cd82f | |
|
8cf7902563 | |
|
997d1ab4af | |
|
11f97f6499 | |
|
304d32e371 | |
|
d4550bd747 | |
|
f2275e4910 | |
|
0fd84b718f | |
|
4bdeea3a64 | |
|
50a7ae45db | |
|
727ee8ab18 | |
|
d8a4896c58 | |
|
a0393efce5 | |
|
291cbbf7f3 | |
|
9f0e22ed35 | |
|
02a5900995 | |
|
d1b19a3435 | |
|
85e730c091 | |
|
5c36d14fb5 | |
|
835a670ea4 | |
|
c0a2bdeca2 | |
|
ef5d1df09f | |
|
919ab09a09 | |
|
bfeb5e1049 | |
|
ca926e181c | |
|
794676f6b7 | |
|
dd3a7ef7fd | |
|
48c2ee0977 | |
|
dc5757c1d5 | |
|
f242925299 | |
|
3c61ada25e | |
|
27bb836c05 | |
|
25a436ddd9 | |
|
e3f6868580 | |
|
64f0a9db02 | |
|
6141ddd9cd | |
|
bd59209c1f | |
|
ed9728ad22 | |
|
ac62acfe19 | |
|
42d3e85d07 | |
|
afd2ac6288 | |
|
8b89dd0d59 | |
|
13ee0c9750 | |
|
f603aafdc1 | |
|
ff599d48e0 | |
|
ba00736856 | |
|
ac6ebe4295 | |
|
7bcbd7ce92 | |
|
a64d321e12 | |
|
2cc7d4c67b | |
|
c4a617fbe2 | |
|
c184c3e784 | |
|
ba2b89a5a8 | |
|
57748d7e93 | |
|
0cf13c978f | |
|
a962937f53 | |
|
2d18956436 | |
|
271d7fde97 | |
|
963ee8c29f | |
|
5d4e61074b | |
|
4b778f5a0e | |
|
92d2dbebcd | |
|
695398787d | |
|
73ec0f6039 | |
|
1b6263b77d | |
|
edb61a1479 | |
|
fcc8382411 | |
|
2d64b25983 | |
|
8e1b6020f2 | |
|
aa8e9a3a7c | |
|
fb6da083c0 | |
|
7c603a7798 | |
|
51e115d596 | |
|
b451634c9a | |
|
28f9079f4a | |
|
37f3e4495f | |
|
9e625a506d | |
|
bca5e97f55 | |
|
7513bcffe1 | |
|
6fabedfd91 | |
|
da2ab7e484 | |
|
20f1ba25a3 | |
|
1a2fe7907e | |
|
467e4fdf41 | |
|
b0ba3fa125 | |
|
351d0baad5 | |
|
30b703c522 | |
|
ec6a4e3134 | |
|
e3986e4a05 | |
|
aae38cd073 | |
|
12a0538f47 | |
|
acdbd1ef0f | |
|
67a720832e | |
|
b2dc619ce1 | |
|
fd50a5b2a7 | |
|
fdf511390b | |
|
adf92ba2af | |
|
5ba02893d5 | |
|
712662c09d | |
|
2e98b1918f | |
|
ae7c165314 | |
|
82a4b09c54 | |
|
30cbd7c17c | |
|
ab0aa7a7cc | |
|
1d15a8f19c | |
|
f11a23b18c | |
|
727edcb308 | |
|
2fd461451c | |
|
27d372b560 | |
|
cf86f7e308 | |
|
40e99c34c6 | |
|
98f9e76b65 | |
|
7239832096 | |
|
4cecb9fe5d | |
|
c25df551ef | |
|
a7eb627dee | |
|
d4101da93f | |
|
d6c526a596 | |
|
149dee490c | |
|
48379a4b0c | |
|
a41847eb0e | |
|
b3c3155424 | |
|
c6cda321c9 | |
|
ae9aeb2c76 | |
|
118ba8f253 | |
|
601da40aae | |
|
90961dda4e | |
|
a359a87e87 | |
|
86d41a6b0f | |
|
13e0bbdec6 | |
|
d65ddb1d4c | |
|
f3e361f86e | |
|
bb020fd5e7 | |
|
6b5202c93b | |
|
72b26ef641 | |
|
8a6f77d0dd | |
|
16cb7120b9 | |
|
ce715ee1e7 | |
|
9efec4495d | |
|
6e160c3dcd | |
|
65dfcf560d | |
|
27c5165371 | |
|
5234ccb0f3 | |
|
04b0752d6f | |
|
ad5095f53f | |
|
d5dbf7575d | |
|
0ed25ec821 | |
|
a4d0dc2ba7 | |
|
573e9201e0 | |
|
ce5b7dd806 | |
|
2fc29492b5 | |
|
9a4d1fcf2b | |
|
fc17ea590c | |
|
9c4c92dcca | |
|
643f3bffb8 | |
|
44e039d285 | |
|
cb85d9250b | |
|
a288f92c89 | |
|
d41609ac0c | |
|
895dcf996b | |
|
aa2523e287 | |
|
34dd948ce9 | |
|
0e18220256 | |
|
09eaefb50b | |
|
cbc3e089e6 | |
|
f90e9c1403 | |
|
e46df6527d | |
|
cd1775a771 | |
|
b72b2a966e | |
|
88719e9b29 | |
|
7f706e5036 | |
|
d4d70e67c2 | |
|
a68b497085 | |
|
afcc4bd1fe | |
|
b010cc3e67 | |
|
8812a8a58d | |
|
6108bcfd6b | |
|
b4ad4b5ace | |
|
aeb073cbf7 | |
|
8d66f245be | |
|
73a9910ad3 | |
|
a0cf0f979a | |
|
edb161b9cc | |
|
98f26e326c | |
|
41b546af09 | |
|
0d06c4e058 | |
|
69a4d0788c | |
|
d9e64ba5db | |
|
0d0f7d195f | |
|
c585e701d3 | |
|
7f1e279043 | |
|
1f901c561c | |
|
97494f1b2e | |
|
d4175b06f3 | |
|
b94d486beb | |
|
a78c65d814 | |
|
90021fe8bb | |
|
92c554b4e1 | |
|
5c2a3b275c | |
|
99b7f1980c | |
|
6870991df3 | |
|
6bb945d919 | |
|
841abb0689 | |
|
7b81d86da4 | |
|
90f512b049 | |
|
5682a2e17e | |
|
21f805bd10 | |
|
25de13003a | |
|
0e638a350a | |
|
7e7afec65d | |
|
cac1ab9578 | |
|
6647eeb088 | |
|
22d5389432 | |
|
c9e229b2f8 | |
|
6a111e450f | |
|
1253e98806 | |
|
d0c31d0b45 | |
|
eea00e3d1d | |
|
3b5351d418 | |
|
7803b54d25 | |
|
1ae9414182 | |
|
6abb48eb80 | |
|
1ce828f33d | |
|
aba7459fbe | |
|
2908008152 | |
|
84d7a632d7 | |
|
c7e9e9ed45 | |
|
de7a2b450b | |
|
89ce3f84ad | |
|
a8ca64b33d | |
|
05cb12ed8e | |
|
23fceff8a0 | |
|
5b35c61cbe | |
|
c8b492ef31 | |
|
8ac8199efc | |
|
5800e77a87 | |
|
d480f3126f | |
|
ca72508ac1 | |
|
774264db1b | |
|
17d866cf53 | |
|
dc8cded242 | |
|
2599b41581 | |
|
9a9e63ad54 | |
|
a33754214e | |
|
271af40402 | |
|
dd9ba881dd | |
|
a4add75ea0 | |
|
14ad433ce6 | |
|
cfe488f4e1 | |
|
73d7fec4a7 | |
|
5f9400c37a | |
|
ffe995975c | |
|
725f85ab8e | |
|
a2d6e9e9a5 | |
|
59b79d81eb | |
|
6d506f9023 | |
|
f416059afa | |
|
4a4a2440df | |
|
7954d70c59 | |
|
594c3ac38a | |
|
1b15f0263b | |
|
5945f5a131 | |
|
b7e3e8325c | |
|
45693e9b31 | |
|
e477a93c0f | |
|
3f8021f09b | |
|
4d71cea292 | |
|
d727751be4 | |
|
9837df11cd | |
|
a8c9296857 | |
|
1867917c92 | |
|
c772697a06 | |
|
946079e062 | |
|
1f02f89003 | |
|
d90ffb2e74 | |
|
3377b68479 | |
|
227a4a15f0 | |
|
2b0995808e | |
|
8142780afe | |
|
363e07c24c | |
|
43e99f10f6 | |
|
395a4cf2f6 | |
|
65a78a3a93 | |
|
04b157fde5 | |
|
33efeaaf24 | |
|
035b14eb91 | |
|
304701fc15 | |
|
5396173c64 | |
|
2d7bc84f81 | |
|
67620039cf | |
|
9a5e4e3244 | |
|
d91d17e324 | |
|
4085bc09ef | |
|
a68e5df801 | |
|
c8f5b90237 | |
|
5729d38f38 | |
|
cf03d1ea46 | |
|
20561687bf | |
|
7dc96728d4 | |
|
72d5923a1e | |
|
c0f17143ab | |
|
c9a8599d2e | |
|
4a8736ec39 | |
|
824697b519 | |
|
fb7c4158f6 | |
|
f801670b73 | |
|
e129d143ff | |
|
2ec8980897 | |
|
1c1d497f5c | |
|
49734ea71e | |
|
6fd871bb91 | |
|
ef6b92052b | |
|
71e1bd08f4 | |
|
67ca38ccf2 | |
|
8fa5334c29 | |
|
a9a23f4212 | |
|
2b64cbf91c | |
|
4937cedcce | |
|
098c1da16f | |
|
60111b9a90 | |
|
25f335afd0 | |
|
19fc65499d | |
|
b99d8de11e | |
|
7c8980e727 | |
|
64fc452cce | |
|
850fc6a171 | |
|
f74229815e | |
|
4db10b5f7f | |
|
31ff9df126 | |
|
a1635f9715 | |
|
1f7664935c | |
|
a2e58b454e | |
|
5ab102f986 | |
|
3639d2b6c2 | |
|
918e53c131 | |
|
e83ec6d687 | |
|
605284446f | |
|
c378bb5579 | |
|
d41e2ce22e | |
|
7487fc5d50 | |
|
fc86621576 | |
|
a0e02e2087 | |
|
4d0f31268b | |
|
c28cf64a15 | |
|
923b08a8a9 | |
|
6da3a0a87e | |
|
61adb2bfb9 | |
|
bf706dfede | |
|
00a29d8ac0 | |
|
ff180aecf5 | |
|
04adf5faaa | |
|
62b2901ed1 | |
|
0e3ccfa381 | |
|
dbdabd088e | |
|
ed0c4ea4d1 | |
|
55212aa8fc | |
|
082f062977 | |
|
00169b7132 | |
|
4bcbf33ddc | |
|
f71ee7cf1e | |
|
fa969734cb | |
|
3261e47bb9 | |
|
82d0f9b4c1 | |
|
d1770dc1ce | |
|
98ff42c709 | |
|
47d2d3e4d9 | |
|
d558b92cfd | |
|
48d7547164 | |
|
74a5d59827 | |
|
e8a51f8007 | |
|
5f1f828a45 | |
|
001dfc5ceb | |
|
ee06e8f09a | |
|
7c1075f8fb | |
|
9140813069 | |
|
5894e03a1e | |
|
22f11d2c9f | |
|
a0fa142a57 | |
|
880825fd9a | |
|
7e0e76a047 | |
|
ecb5f03b70 | |
|
4c05cd29f7 | |
|
e91176d3f0 | |
|
65a97ae351 | |
|
730235f7ed | |
|
e9317f48c7 | |
|
424cfd7fa5 | |
|
6c3dc65413 | |
|
6c84e980b6 | |
|
23e43c770e | |
|
2ec87ba9da | |
|
5f2bab7e09 | |
|
181b67beac | |
|
502d72e517 | |
|
8198bfd36e | |
|
8af7dd2115 | |
|
e3555b7f61 | |
|
371a301175 | |
|
223980844d | |
|
488294a253 | |
|
def67eea14 | |
|
ed12182164 | |
|
fa74c7bea0 | |
|
5cd8748a46 | |
|
f46764ae39 | |
|
d25f5c0d63 | |
|
437f5f0e80 | |
|
72a2c650e4 | |
|
842ae70d35 | |
|
9ea39e1817 | |
|
c1f13fa086 | |
|
bb767b31f0 | |
|
09ee7b1398 | |
|
8d025a2f31 | |
|
b43e989bb8 | |
|
b9973c4f5c | |
|
d4997336ff | |
|
7f7aeef6a3 | |
|
20dcde2658 | |
|
c6593405e3 | |
|
418c11b0dd | |
|
dfa5f89ab9 | |
|
311a84270c | |
|
ed0a3bca4d | |
|
d9b4519df4 | |
|
65857ba1ed | |
|
8e55c863bc | |
|
29d1c49518 | |
|
c151dc9cdf | |
|
d7853c2a2e | |
|
d5e731d284 | |
|
fb7a6b5f89 | |
|
4c56ca9c29 | |
|
b7c8cfac3d | |
|
1a03203aec | |
|
edcb1d9fbc | |
|
2055bc93a2 | |
|
449bc65d22 | |
|
bc478ae9a5 | |
|
4c7d87655f | |
|
e2870d2d53 | |
|
ed5d74973f | |
|
a5991cc5af | |
|
53ceca3ef4 | |
|
a3c8f261b7 | |
|
8ddd4e0bb5 | |
|
aaf72d5ff5 | |
|
9aa4659830 | |
|
ad2bc2707a | |
|
47263dc54c | |
|
7fff00a49d | |
|
3dc256b545 | |
|
148bf6d56f | |
|
b8d2e743ab | |
|
f3c85c7135 | |
|
8f8265400c | |
|
82c5dbb153 | |
|
ab1f09cf32 | |
|
a845a579c5 | |
|
eec9f7f12f | |
|
b36cab207a | |
|
7a0e1bf8c4 | |
|
b1aa6695d0 | |
|
a78ecc284b | |
|
7a88df79c6 | |
|
a69bd5c054 | |
|
fdab914431 | |
|
ef5605e448 | |
|
e6b4ad3250 | |
|
726d3b3180 | |
|
7939c663dc | |
|
c9458a9a23 | |
|
e03d865fea | |
|
33f8542946 | |
|
a394783c25 | |
|
e2f28fc2f6 | |
|
5454925fcf | |
|
eb11fd2e60 | |
|
c59ce3b918 | |
|
c39e00bad1 | |
|
3a20db8a07 | |
|
e7fd816177 | |
|
165ed79b9e | |
|
e76a9c8959 | |
|
12ff71a5ef | |
|
a4912e6e41 | |
|
cc67faa3ec | |
|
ec1ee65dbf | |
|
dc48ec7a5e | |
|
01d4d0c8cf | |
|
59ae7e7195 | |
|
0018e11eab | |
|
82b44a5498 | |
|
e56e088223 | |
|
4a6c0b0839 | |
|
619334a487 | |
|
299dc40ccb | |
|
ede4149e2b | |
|
2f1ce83497 | |
|
2b34489a39 | |
|
e621cdef8a | |
|
82d55f592f | |
|
e7cf2dd3fe | |
|
e5999d5d95 | |
|
cbb6f09bb8 | |
|
a28d2ea0db | |
|
09bb1cdcb6 | |
|
9ba5714f3a | |
|
e0b53db70d | |
|
e5d5e00826 | |
|
8b513dbf44 | |
|
7d5149b5df | |
|
b28df22449 | |
|
2d7879006e | |
|
9ae548f99a | |
|
ff50170598 | |
|
05fc47e66b | |
|
e0127c013b | |
|
ae61c93241 | |
|
76448f655d | |
|
03a68c9a3f | |
|
23294ff237 | |
|
092a05d986 | |
|
374b8181af | |
|
4f3f56e31b | |
|
cd5e5a7c56 | |
|
cea8275dfd | |
|
af07aa563a | |
|
91d9228eb4 | |
|
ddc03c450f | |
|
74c817834e | |
|
b83ab40d2e | |
|
5617a9cd02 | |
|
d105c54842 | |
|
06bf9f81dc | |
|
a585964675 | |
|
512b7bbc16 | |
|
fcf2176e32 | |
|
ae15a1fe1c | |
|
6049151a16 | |
|
83e40f6d2e | |
|
d7e9058554 | |
|
8a721aff76 | |
|
4239294165 | |
|
5a12b4c28c | |
|
720c3e20a3 | |
|
64372006e0 | |
|
468dab8c4c | |
|
4e581ae16d | |
|
98e4130941 | |
|
41b00b5ee5 | |
|
70fa2e69e2 | |
|
850e8b3153 | |
|
f95e571b76 | |
|
df54e7d7b4 | |
|
b9dd0a7877 | |
|
6052949972 | |
|
fd44713ff4 | |
|
a30206bb75 | |
|
64782b6ad6 | |
|
ce08518084 | |
|
44a33d80eb | |
|
ad7c601a48 | |
|
d483791489 | |
|
f9c823cdbc | |
|
7ac0cfa1a2 | |
|
06e7657e82 | |
|
c874434c2f | |
|
48118b5904 | |
|
e10657d2a6 | |
|
94682c1353 | |
|
9340457fcc | |
|
4045f4c7db | |
|
29c380e4f4 | |
|
23adfc639b | |
|
7f2a2ec642 | |
|
ac58ccc4d8 | |
|
db2ccfa860 | |
|
cc26464eca | |
|
ff3e538984 | |
|
192fa2e295 | |
|
999a5f01f5 | |
|
339150646c | |
|
445ef30c69 | |
|
23ebcbfdf6 | |
|
7ccebbb8b1 | |
|
cf2fd4e55b | |
|
38133f7fca | |
|
c3dc248ce8 | |
|
9baef1b1ec | |
|
23ca6638d0 | |
|
1e46fc4953 | |
|
71858ce620 | |
|
2679a1373a | |
|
0c3bad31fc | |
|
e328553f59 | |
|
dd6f81c288 | |
|
6cb1456b1a | |
|
299308348e | |
|
3dd350c3ce | |
|
ea1c088bea | |
|
fc2ff7c9c5 | |
|
0265204804 | |
|
410881df4e | |
|
2a7f89a958 | |
|
cae6574234 | |
|
f03647b772 | |
|
fced53e13b | |
|
95d545c53e | |
|
9e4bf897b5 | |
|
567735e093 | |
|
d51d6fe48d | |
|
2f25a9fe7f | |
|
449f5da773 | |
|
5c63bcb51d | |
|
411ccb5d57 | |
|
9c38833287 | |
|
5bc3896171 | |
|
246e274410 | |
|
ea3c623f7b | |
|
ea493016c7 | |
|
b7243c9979 | |
|
c52ae1dd1a | |
|
48d3f9ce7a | |
|
eca531afdd | |
|
fd9a3aeb6f | |
|
bbf66174de | |
|
fd64cbc4b9 | |
|
dc0d6d0b10 | |
|
38af7328a0 | |
|
48c079aa85 | |
|
f693470827 | |
|
c9ac99b947 | |
|
b7db9f42cb | |
|
2dabffa6d2 | |
|
29e516ec96 | |
|
bc5bf4e6d6 | |
|
aeb60c7465 | |
|
9204af1e8d | |
|
a4db51a944 | |
|
70865c526e | |
|
08204190ff | |
|
91ae3a3134 | |
|
d8f1cbcca6 | |
|
348e710b34 | |
|
3fc404c8e4 | |
|
ff4e56277f | |
|
fca61d2e55 | |
|
61603036ed | |
|
c575ab0331 | |
|
16aff14d0c | |
|
3f6104973b | |
|
78c3429dd8 | |
|
302c7051a0 | |
|
456985ccfc | |
|
6c659f8cf4 | |
|
2e3f93ae82 | |
|
2757bdefe1 | |
|
a0143c38cf | |
|
94fd8b0643 | |
|
89994b15b7 | |
|
348b9cc59b | |
|
2c541dfe25 | |
|
64a8b12b2b | |
|
4aec97ed27 | |
|
c4bfe5faf3 | |
|
fe709ecadd | |
|
673a368454 | |
|
3ef78a10e3 | |
|
ac79879512 | |
|
454347e15c | |
|
a5aef8c798 | |
|
245f333448 | |
|
2913b0fcbd | |
|
98129e963a | |
|
ac00e474f3 | |
|
0a7e623356 | |
|
c3720aee5c | |
|
555e651c89 | |
|
c0d001df3f | |
|
81ce01f0df | |
|
d554176487 | |
|
e414ea38cc | |
|
ecc865f690 | |
|
89f29a2a74 | |
|
ed36c9c16a | |
|
b31d90de19 | |
|
8730c7808a | |
|
d60fc52aa5 | |
|
aa5fe68456 | |
|
c77e5d24fe | |
|
a12d5ce067 | |
|
f47ad82485 | |
|
045377e130 | |
|
1b42462345 | |
|
c638304960 | |
|
cd72a7a40a | |
|
fcdfecf8a1 | |
|
26d5bbb3c3 | |
|
ddcc7c7589 | |
|
6563d0f435 | |
|
0c49869d10 | |
|
d790c05b8f | |
|
e35c2b6d61 | |
|
661a2a3ecd | |
|
be94b251bc | |
|
16866af9c0 | |
|
4abf37b6ed | |
|
41ecfc33ed | |
|
942441eb28 | |
|
9428a46a7b | |
|
07d7901055 | |
|
5abf1cfc2b | |
|
2252c2e08e | |
|
3ccb76f9a7 | |
|
3bd3efe4af | |
|
e7283195ea |
|
@ -1,204 +0,0 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: litmus-portal-admin-config
|
||||
data:
|
||||
AgentScope: cluster
|
||||
AgentNamespace: litmus
|
||||
DataBaseServer: "mongodb://mongo-service:27017"
|
||||
JWTSecret: "litmus-portal@123"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: litmusportal-frontend
|
||||
labels:
|
||||
component: litmusportal-frontend
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
component: litmusportal-frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
component: litmusportal-frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: litmusportal-frontend
|
||||
image: #{portal-frontend}
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: litmusportal-frontend-service
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- name: http
|
||||
port: 9091
|
||||
targetPort: 8080
|
||||
selector:
|
||||
component: litmusportal-frontend
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: litmusportal-server
|
||||
labels:
|
||||
component: litmusportal-server
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
component: litmusportal-server
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
component: litmusportal-server
|
||||
spec:
|
||||
containers:
|
||||
- name: graphql-server
|
||||
image: #{portal-server}
|
||||
env:
|
||||
- name: DB_SERVER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: DataBaseServer
|
||||
- name: JWT_SECRET
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: JWTSecret
|
||||
- name: SELF_CLUSTER
|
||||
value: "false"
|
||||
- name: AGENT_SCOPE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: AgentScope
|
||||
- name: AGENT_NAMESPACE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: AgentNamespace
|
||||
- name: LITMUS_PORTAL_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: PORTAL_SCOPE
|
||||
value: "cluster"
|
||||
- name: SUBSCRIBER_IMAGE
|
||||
value: #{subscriber}
|
||||
- name: ARGO_SERVER_IMAGE
|
||||
value: "argoproj/argocli:v2.9.3"
|
||||
- name: ARGO_WORKFLOW_CONTROLLER_IMAGE
|
||||
value: "argoproj/workflow-controller:v2.9.3"
|
||||
- name: ARGO_WORKFLOW_EXECUTOR_IMAGE
|
||||
value: "argoproj/argoexec:v2.9.3"
|
||||
- name: LITMUS_CHAOS_OPERATOR_IMAGE
|
||||
value: "litmuschaos/chaos-operator:1.8.2"
|
||||
- name: LITMUS_CHAOS_RUNNER_IMAGE
|
||||
value: "litmuschaos/chaos-runner:1.8.2"
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
imagePullPolicy: Always
|
||||
- name: auth-server
|
||||
image: #{auth-server}
|
||||
env:
|
||||
- name: DB_SERVER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: DataBaseServer
|
||||
- name: JWT_SECRET
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: JWTSecret
|
||||
- name: ADMIN_USERNAME
|
||||
value: "admin"
|
||||
- name: ADMIN_PASSWORD
|
||||
value: "litmus"
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
imagePullPolicy: Always
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: litmusportal-server-service
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- name: graphql-server
|
||||
port: 9002
|
||||
targetPort: 8080
|
||||
- name: auth-server
|
||||
port: 9003
|
||||
targetPort: 3000
|
||||
selector:
|
||||
component: litmusportal-server
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mongo
|
||||
labels:
|
||||
app: mongo
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
component: database
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
component: database
|
||||
spec:
|
||||
containers:
|
||||
- name: mongo
|
||||
image: mongo:4.2.8
|
||||
ports:
|
||||
- containerPort: 27017
|
||||
imagePullPolicy: Always
|
||||
volumeMounts:
|
||||
- name: mongo-persistent-storage
|
||||
mountPath: /data/db
|
||||
volumes:
|
||||
- name: mongo-persistent-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: mongo-pv-claim
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: mongo-pv-claim
|
||||
labels:
|
||||
app: mongo
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 20Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: mongo
|
||||
name: mongo-service
|
||||
spec:
|
||||
ports:
|
||||
- port: 27017
|
||||
targetPort: 27017
|
||||
selector:
|
||||
component: database
|
||||
---
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"cmd":["echo Litmus-Portal"],
|
||||
"build":[{
|
||||
"name":"portal-frontend",
|
||||
"file":"litmus-portal/frontend/Dockerfile",
|
||||
"context":"litmus-portal/frontend",
|
||||
"push": true
|
||||
},{
|
||||
"name":"portal-server",
|
||||
"file":"litmus-portal/graphql-server/build/Dockerfile",
|
||||
"context":"litmus-portal/graphql-server",
|
||||
"push":true
|
||||
},{
|
||||
"name":"auth-server",
|
||||
"file":"litmus-portal/authentication/Dockerfile",
|
||||
"context":"litmus-portal/authentication",
|
||||
"push":true
|
||||
},{
|
||||
"name":"subscriber",
|
||||
"file":"litmus-portal/cluster-agents/subscriber/build/Dockerfile",
|
||||
"context":"litmus-portal/cluster-agents/subscriber",
|
||||
"push":true
|
||||
}],
|
||||
"k8s-manifest":".betterci/ci-k8s-manifest.yml"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
component_depth: 2
|
||||
languages:
|
||||
- go
|
||||
- typescript
|
||||
exclude:
|
||||
- /build/plugins/.*
|
||||
- /build/utils/.*
|
||||
- /litmus-portal/graphql-server/graph/generated/.*
|
||||
- /litmus-portal/graphql-server/graph/model/.*
|
||||
test:
|
||||
include:
|
||||
- /litmus-portal/frontend/src/.*
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
exclude_paths:
|
||||
- "chaoscenter/web/src/api/auth/**"
|
|
@ -0,0 +1,6 @@
|
|||
*.go linguist-detectable=true
|
||||
*.css linguist-detectable=false
|
||||
*.js linguist-detectable=false
|
||||
*.ts linguist-detectable=false
|
||||
*.html linguist-detectable=false
|
||||
*.tsx linguist-detectable=false
|
|
@ -0,0 +1,119 @@
|
|||
## How to Build LitmusPortal Docker Images?
|
||||
|
||||
The litmusportal runs on top of Kubernetes and is built on a set of docker containers it provides you the flexibility to build a custom image to visualize/check
|
||||
your changes. Here are the components for which you can create your custom Docker images from this repository:
|
||||
|
||||
- GraphQL Server
|
||||
- Cluster Agents: Subscriber, Event Tracker
|
||||
- Web UI (Frontend)
|
||||
|
||||
Follow the given steps to build custom Docker images:
|
||||
|
||||
**Clone litmus repository**
|
||||
|
||||
- We need to clone (forked or base) the litmus repository and make the required changes (if any).
|
||||
|
||||
```bash
|
||||
git clone http://github.com/litmuschaos/litmus
|
||||
cd litmus/litmus-portal
|
||||
```
|
||||
|
||||
- The litmus portal component also supports the multiarch builds that are the builds on different `OS` and `ARCH`. Currently, the images are tested to be working
|
||||
for `linux/amd64` and `linux/arm64`builds.
|
||||
|
||||
#### Docker Image Build Tunables
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th> Variables </th>
|
||||
<th> Description </th>
|
||||
<th> Example </th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> REPONAME </td>
|
||||
<td> Provide the DockerHub user/organisation name of the image. </td>
|
||||
<td> <code>REPONAME=example-repo-name</code> <br> used as <code>example-repo-name/litmusportal-server:ci</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> IMAGE_NAME </td>
|
||||
<td> Provide the custom image name for the specific component. </td>
|
||||
<td> <code>IMAGE_NAME=example-image-name</code> <br> used as <code>litmuschaos/example-image-name:ci</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> IMAGE_TAG </td>
|
||||
<td> Provide the custom image tag for the specific build. </td>
|
||||
<td> <code>IMAGE_TAG=example-tag</code> <br> used as <code>litmuschaos/litmusportal-server:example-tag</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> PLATFORMS </td>
|
||||
<td> Provide the target platforms for the image as CSV. <br>The tested ones are <code>linux/amd64</code> and <code>linux/arm64</code> </td>
|
||||
<td> <code>PLATFORMS=linux/amd64,linux/arm64</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> DIRECTORY </td>
|
||||
<td> Provide the directory according to the component directory structure. </td>
|
||||
<td> Different <code>DIRECTORY</code> values are:<br>
|
||||
<li> <b>GraphQL Server:</b> "graphql-server" <br>
|
||||
<li> <b>Subscriber:</b> "cluster-agents/subscriber" <br>
|
||||
<li> <b>Event Tracker:</b> "cluster-agents/event-tracker" <br>
|
||||
<li> <b>Frontend:</b> N/A</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### For AMD64 Build
|
||||
|
||||
- To build only amd64 image export the variables from the above table.
|
||||
- Run `make push-portal-component-amd64`
|
||||
|
||||
- For frontend image export the `timestamp` env with the current time and run `make push-frontend-amd64`.
|
||||
|
||||
OR
|
||||
|
||||
- Fill the ENVs from the above table in the given command and execute it.
|
||||
|
||||
```bash
|
||||
cd ${DIRECTORY}
|
||||
docker build . -f Dockerfile -t ${REPONAME}/${IMAGE_NAME}:${IMG_TAG} --build-arg TARGETARCH=amd64
|
||||
docker push ${REPONAME}/${IMAGE_NAME}:${IMG_TAG}
|
||||
```
|
||||
|
||||
For frontend image:
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
docker build . -f Dockerfile -t $(REPONAME)/$(IMAGE_NAME):${IMG_TAG} --build-arg TARGETARCH=amd64 --build-arg REACT_APP_KB_CHAOS_VERSION=${IMG_TAG} \
|
||||
--build-arg REACT_APP_BUILD_TIME="${timestamp}" --build-arg REACT_APP_HUB_BRANCH_NAME="v1.13.x
|
||||
|
||||
docker push $(REPONAME)/$(IMAGE_NAME):$(IMG_TAG)
|
||||
```
|
||||
|
||||
### For building multi-arch images
|
||||
|
||||
- For building multi-arch image setup [docker buildx](https://docs.docker.com/buildx/working-with-buildx/) in your system. You can also check out this [blog](https://dev.to/uditgaurav/multiarch-support-in-litmuschaos-34da) for the same.
|
||||
|
||||
- Once the docker buildx is setup export all the target platforms on which you want to deploy your images as a CSV Like `export PLATFORMS=linux/amd4,linux/arm64` along with the ENVs mentioned
|
||||
in the above table.
|
||||
- Build and push the multi-arch image using:
|
||||
|
||||
```bash
|
||||
make push-portal-component
|
||||
```
|
||||
|
||||
- For frontend image export the `timestamp` ENV with the current time and run `make push-frontend`.
|
||||
|
||||
OR
|
||||
|
||||
- Fill the ENVs from the above table in the given command and execute it.
|
||||
|
||||
```bash
|
||||
cd ${DIRECTORY}
|
||||
docker buildx build -f Dockerfile --progress plain --push --no-cache --platform ${PLATFORMS} -t ${REPONAME}/$(IMAGE_NAME):$(IMG_TAG} .
|
||||
```
|
||||
|
||||
For frontend image:
|
||||
|
||||
```bash
|
||||
cd ${DIRECTORY}
|
||||
docker buildx build . -f Dockerfile --progress plain --push --no-cache --platform ${PLATFORMS} -t ${REPONAME}/${IMAGE_NAME}:${IMG_TAG} \
|
||||
--build-arg REACT_APP_KB_CHAOS_VERSION=${IMG_TAG} --build-arg REACT_APP_BUILD_TIME="${timestamp}" --build-arg REACT_APP_HUB_BRANCH_NAME="v1.13.x"
|
||||
```
|
|
@ -1,7 +1,9 @@
|
|||
---
|
||||
name: "\U0001f41b Bug report"
|
||||
name: "\U0001F41B Bug report"
|
||||
about: Create a report to help improve the project
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
@ -20,6 +22,9 @@ labels: bug
|
|||
|
||||
**What you expected to happen**:
|
||||
|
||||
**Where can this issue be corrected? (optional)**
|
||||
<!-- provide additional information about the file location or directory that needs to be modified to fix this issue, if available -->
|
||||
|
||||
**How to reproduce it (as minimally and precisely as possible)**:
|
||||
|
||||
**Anything else we need to know?**:
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
---
|
||||
name: "\U0001f6f8 Feature request"
|
||||
name: "\U0001F6F8 Feature request"
|
||||
about: Suggest an idea that will improve the project
|
||||
labels: enhancement
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
name: Organization Maintainer Request
|
||||
about: Request to become a maintianer in LitmusChaos Org
|
||||
title: "REQUEST: Promote <your-GH-handle> to maintainer for LitmusChaos"
|
||||
labels: type/maintainer-request
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
### GitHub Username
|
||||
|
||||
@<your-GH-handle>
|
||||
|
||||
### Requirements
|
||||
|
||||
- [ ] I have reviewed [the community role guidelines](/community-roles.md)
|
||||
- [ ] I have [enabled 2FA on my GitHub account](https://github.com/settings/security)
|
||||
- [ ] I am an active member of 1 or more LitmusChaos subprojects for atleast the last 6 months.
|
||||
- [ ] I am an active participant in issue/PR reviews for atleast 2 subprojects and for the past 6 months.
|
||||
- [ ] I have been involved in technical and project discussions with other maintainers.
|
||||
- [ ] I have atleast one sponsor that meet the sponsor requirements listed in the community role guidelines
|
||||
- [ ] I have spoken to my sponsor ahead of this application, and they have agreed to sponsor my application
|
||||
- [ ] I understand that I can [make my membership public](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/publicizing-or-hiding-organization-membership) if I'd like to once I am invited to the organization
|
||||
|
||||
### Sponsors
|
||||
|
||||
- @<sponsor-1>
|
||||
- @<sponsor-2>
|
||||
|
||||
### List of contributions to the LitmusChaos project
|
||||
|
||||
- PRs reviewed / authored
|
||||
- Issues responded to
|
||||
- Projects I am involved with
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
name: Organization Member Request
|
||||
about: Request membership in LitmusChaos Org
|
||||
title: "REQUEST: New member request for <your-GH-handle>"
|
||||
labels: type/member-request
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
### GitHub Username
|
||||
|
||||
@<your-GH-handle>
|
||||
|
||||
### Requirements
|
||||
|
||||
- [ ] I have reviewed [the community role guidelines](/community-roles.md)
|
||||
- [ ] I have [enabled 2FA on my GitHub account](https://github.com/settings/security)
|
||||
- [ ] I am actively contributing to 1 or more LitmusChaos subprojects
|
||||
- [ ] I have atleast one sponsor that meet the sponsor requirements listed in the community role guidelines
|
||||
- [ ] I have spoken to my sponsor ahead of this application, and they have agreed to sponsor my application
|
||||
- [ ] I understand that I can [make my membership public](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/publicizing-or-hiding-organization-membership) if I'd like to once I am invited to the organization
|
||||
|
||||
### Sponsors
|
||||
|
||||
- @<sponsor-1>
|
||||
- @<sponsor-2>
|
||||
|
||||
### List of contributions to the LitmusChaos project
|
||||
|
||||
- PRs reviewed / authored
|
||||
- Issues responded to
|
||||
- Projects I am involved with
|
|
@ -1,7 +1,10 @@
|
|||
---
|
||||
name: "\U0001F914 Question"
|
||||
about: Question not answered in our community meetings, Docs or Readme.
|
||||
labels: question
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Question
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
name: Organization Reviewer Request
|
||||
about: Request reviewer membership in LitmusChaos Org
|
||||
title: "REQUEST: Promote <your-GH-handle> to reviewer for LitmusChaos"
|
||||
labels: type/reviewer-request
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
### GitHub Username
|
||||
|
||||
@<your-GH-handle>
|
||||
|
||||
### Requirements
|
||||
|
||||
- [ ] I have reviewed [the community role guidelines](/community-roles.md)
|
||||
- [ ] I have [enabled 2FA on my GitHub account](https://github.com/settings/security)
|
||||
- [ ] I am an active member of 1 or more LitmusChaos subprojects for atleast the last 3 months.
|
||||
- [ ] I am an active participant in issue/PR reviews for atleast 1 month.
|
||||
- [ ] I have reviewed or authored atleast 5 significant PRs.
|
||||
- [ ] I have atleast one sponsor that meet the sponsor requirements listed in the community role guidelines
|
||||
- [ ] I have spoken to my sponsor ahead of this application, and they have agreed to sponsor my application
|
||||
- [ ] I understand that I can [make my membership public](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/publicizing-or-hiding-organization-membership) if I'd like to once I am invited to the organization
|
||||
|
||||
### Sponsors
|
||||
|
||||
- @<sponsor-1>
|
||||
- @<sponsor-2>
|
||||
|
||||
### List of contributions to the LitmusChaos project
|
||||
|
||||
- PRs reviewed / authored
|
||||
- Issues responded to
|
||||
- Projects I am involved with
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
name: Security Issue
|
||||
about: Report the potential vulnerability or security issue
|
||||
title: 'seclog: [Event Description] '
|
||||
labels: security, vulnerability
|
||||
assignees: Saranya-jena, Jonsy13, SarthakJain26
|
||||
|
||||
---
|
||||
|
||||
### Summary
|
||||
_Short summary of the problem. Make the impact and severity as clear as possible. For example: An unsafe deserialization vulnerability allows any unauthenticated user to execute arbitrary code on the server._
|
||||
|
||||
### Details
|
||||
_Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer._
|
||||
|
||||
### PoC
|
||||
_Complete instructions, including specific configuration details, to reproduce the vulnerability._
|
||||
|
||||
### Impact
|
||||
_What kind of vulnerability is it? Who is impacted?_
|
||||
|
||||
### Remediation
|
||||
_Propose a remediation suggestion if you have one. Make it clear that this is just a suggestion, as the maintainer might have a better idea to fix the issue._
|
|
@ -0,0 +1,21 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "chaoscenter/authentication"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "chaoscenter/event-tracker"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "chaoscenter/subscriber"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "chaoscenter/graphql/server"
|
||||
schedule:
|
||||
interval: "daily"
|
|
@ -3,23 +3,9 @@ set -e
|
|||
|
||||
working_dir="litmus-portal"
|
||||
|
||||
# Array of Image Names
|
||||
image_names=("litmusportal-frontend:ci" "litmusportal-server:ci" "litmusportal-auth-server:ci")
|
||||
|
||||
# Array of DockerFile Paths
|
||||
dockerfile_paths=("frontend" "graphql-server/build" "authentication")
|
||||
|
||||
# Array of directories, for which images have to be build if changed
|
||||
directory_array=("frontend" "graphql-server" "authentication")
|
||||
|
||||
declare -A MYMAP=( [frontend]="litmusportal-frontend:ci" [graphql-server]="litmusportal-server:ci" [authentication]="litmusportal-auth-server:ci" )
|
||||
# Building the images on the basic of changes in paths
|
||||
for i in "${!directory_array[@]}"
|
||||
do
|
||||
current_dir=$(echo "$working_dir/${directory_array[$i]}")
|
||||
nofchanges=$(echo $changed_data | jq -r '[.[]."filename"] | join("\n")' | tr -d '"' | grep ^$current_dir | wc -l)
|
||||
if [ $nofchanges != 0 ]
|
||||
then
|
||||
docker build $current_dir -t litmuschaos/${image_names[$i]} -f $working_dir/${dockerfile_paths[$i]}/Dockerfile
|
||||
kind load docker-image litmuschaos/${image_names[$i]} --name kind
|
||||
fi
|
||||
done
|
||||
current_dir=$(echo "$working_dir/$directory")
|
||||
mkdir Images
|
||||
DOCKER_BUILDKIT=1 docker build $current_dir -t litmuschaos/${MYMAP[$directory]} -f $working_dir/${directory}/Dockerfile
|
||||
docker save "litmuschaos/${MYMAP[$directory]}" > Images/${directory}.tar
|
|
@ -4,7 +4,9 @@ on:
|
|||
branches:
|
||||
- master
|
||||
- v*
|
||||
- litmus-v2
|
||||
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1 # Enable Docker_buildkit in all build jobs
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
|
@ -14,39 +16,60 @@ jobs:
|
|||
frontend: ${{ steps.filter.outputs.frontend }}
|
||||
graphql-server: ${{ steps.filter.outputs.graphql-server }}
|
||||
authentication: ${{ steps.filter.outputs.authentication }}
|
||||
event-tracker: ${{ steps.filter.outputs.event-tracker }}
|
||||
subscriber: ${{ steps.filter.outputs.subscriber }}
|
||||
event-tracker: ${{ steps.filter.outputs.event-tracker }}
|
||||
dex-server: ${{ steps.filter.outputs.dex-server }}
|
||||
steps:
|
||||
# For pull requests it's not necessary to checkout the code
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
frontend:
|
||||
- 'litmus-portal/frontend/**'
|
||||
graphql-server:
|
||||
- 'litmus-portal/graphql-server/**'
|
||||
authentication:
|
||||
- 'litmus-portal/authentication/**'
|
||||
event-tracker:
|
||||
- 'litmus-portal/cluster-agents/event-tracker/**'
|
||||
subscriber:
|
||||
- 'litmus-portal/cluster-agents/subscriber/**'
|
||||
# For pull requests it's not necessary to checkout the code
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
frontend:
|
||||
- 'chaoscenter/web/**'
|
||||
graphql-server:
|
||||
- 'chaoscenter/graphql/server/**'
|
||||
authentication:
|
||||
- 'chaoscenter/authentication/**'
|
||||
subscriber:
|
||||
- 'chaoscenter/subscriber/**'
|
||||
event-tracker:
|
||||
- 'chaoscenter/event-tracker/**'
|
||||
dex-server:
|
||||
- 'chaoscenter/dex-server/**'
|
||||
|
||||
gitleaks-scan:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run GitLeaks
|
||||
run: |
|
||||
wget https://github.com/gitleaks/gitleaks/releases/download/v8.18.2/gitleaks_8.18.2_linux_x64.tar.gz && \
|
||||
tar -zxvf gitleaks_8.18.2_linux_x64.tar.gz && \
|
||||
sudo mv gitleaks /usr/local/bin && gitleaks detect --source . -v
|
||||
|
||||
backend-checks:
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: needs.changes.outputs.graphql-server == 'true' || needs.changes.outputs.authentication == 'true' || needs.changes.outputs.event-tracker == 'true' || needs.changes.outputs.subscriber == 'true'
|
||||
if: needs.changes.outputs.graphql-server == 'true' || needs.changes.outputs.authentication == 'true' || needs.changes.outputs.subscriber == 'true' || needs.changes.outputs.event-tracker == 'true' || needs.changes.outputs.dex-server == 'true'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.22" # By default, the go version is v1.15 in runner.
|
||||
|
||||
- name: Check Golang imports order
|
||||
uses: Jerome1337/goimports-action@v1.0.3
|
||||
with:
|
||||
goimports-path: ./litmus-portal
|
||||
- shell: bash
|
||||
goimports-path: ./chaoscenter
|
||||
|
||||
- name: Backend checks
|
||||
shell: bash
|
||||
run: |
|
||||
cd litmus-portal
|
||||
cd chaoscenter
|
||||
make backend-services-checks
|
||||
|
||||
frontend-checks:
|
||||
|
@ -55,72 +78,131 @@ jobs:
|
|||
if: ${{ needs.changes.outputs.frontend == 'true' }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Frontend checks
|
||||
shell: bash
|
||||
run: |
|
||||
cd litmus-portal
|
||||
cd chaoscenter
|
||||
make frontend-services-checks
|
||||
|
||||
backend-unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- changes
|
||||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.22" # By default, the go version is v1.15 in runner.
|
||||
- name: Backend unit tests
|
||||
shell: bash
|
||||
run: |
|
||||
cd chaoscenter
|
||||
make backend-unit-tests
|
||||
|
||||
web-unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- changes
|
||||
- frontend-checks
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Chaoscenter web unit tests
|
||||
shell: bash
|
||||
run: |
|
||||
cd chaoscenter
|
||||
make web-unit-tests
|
||||
|
||||
docker-build-graphql-server:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- backend-checks
|
||||
- changes
|
||||
# - backend-unit-tests
|
||||
if: ${{ needs.changes.outputs.graphql-server == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build graphql server docker image
|
||||
shell: bash
|
||||
run: |
|
||||
cd litmus-portal/graphql-server
|
||||
docker build . -f build/Dockerfile
|
||||
cd chaoscenter/graphql/server
|
||||
docker build . -f Dockerfile -t docker.io/litmuschaos/litmusportal-server:${{ github.sha }} --build-arg TARGETARCH=amd64
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-server:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
docker-build-authentication-server:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- backend-checks
|
||||
- changes
|
||||
# - backend-unit-tests
|
||||
if: ${{ needs.changes.outputs.authentication == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build auth server docker image
|
||||
shell: bash
|
||||
run: |
|
||||
cd litmus-portal/authentication
|
||||
docker images && docker build . -f Dockerfile
|
||||
cd chaoscenter/authentication
|
||||
docker build . -f Dockerfile -t docker.io/litmuschaos/litmusportal-auth-server:${{ github.sha }} --build-arg TARGETARCH=amd64
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-auth-server:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
docker-build-subscriber:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- backend-checks
|
||||
- changes
|
||||
# - backend-unit-tests
|
||||
if: ${{ needs.changes.outputs.subscriber == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build subscriber docker image
|
||||
shell: bash
|
||||
run: |
|
||||
cd litmus-portal/cluster-agents/subscriber
|
||||
docker build . -f build/Dockerfile
|
||||
cd chaoscenter/subscriber
|
||||
docker build . -f Dockerfile -t docker.io/litmuschaos/litmusportal-subscriber:${{ github.sha }} --build-arg TARGETARCH=amd64
|
||||
|
||||
docker-build-event-tracker:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- backend-checks
|
||||
- changes
|
||||
if: ${{ needs.changes.outputs.event-tracker == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build event tracker docker image
|
||||
shell: bash
|
||||
run: |
|
||||
cd litmus-portal/cluster-agents/event-tracker
|
||||
docker build . -f Dockerfile
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-subscriber:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
docker-build-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -130,9 +212,77 @@ jobs:
|
|||
if: ${{ needs.changes.outputs.frontend == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build frontend docker image
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: yarn build check
|
||||
run: |
|
||||
cd chaoscenter/web && yarn && yarn build
|
||||
|
||||
- name: web docker build check
|
||||
shell: bash
|
||||
run: |
|
||||
cd litmus-portal/frontend
|
||||
docker build . -f Dockerfile
|
||||
cd chaoscenter/web
|
||||
docker build . -f Dockerfile --build-arg TARGETARCH=amd64 -t docker.io/litmuschaos/litmusportal-frontend:${{ github.sha }}
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-frontend:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
docker-build-event-tracker:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- backend-checks
|
||||
- changes
|
||||
# - backend-unit-tests
|
||||
if: ${{ needs.changes.outputs.event-tracker == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build event tracker docker image
|
||||
shell: bash
|
||||
run: |
|
||||
cd chaoscenter/event-tracker
|
||||
docker build . -f Dockerfile -t docker.io/litmuschaos/litmusportal-event-tracker:${{ github.sha }} --build-arg TARGETARCH=amd64
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-event-tracker:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '1'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
docker-build-dex-server:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- backend-checks
|
||||
- changes
|
||||
if: ${{ needs.changes.outputs.dex-server == 'true' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build dex-server docker image
|
||||
shell: bash
|
||||
run: |
|
||||
cd chaoscenter/dex-server
|
||||
docker images && docker build . -f Dockerfile -t docker.io/litmuschaos/litmusportal-dex-server:${{ github.sha }} --build-arg TARGETARCH=amd64
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
image-ref: 'docker.io/litmuschaos/litmusportal-dex-server:${{ github.sha }}'
|
||||
format: 'table'
|
||||
exit-code: '0'
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
name: CIFuzz
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'chaoscenter/authentication/**'
|
||||
- 'chaoscenter/graphql/**'
|
||||
- 'chaoscenter/subscriber/**'
|
||||
permissions: {}
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
sanitizer: [address]
|
||||
steps:
|
||||
- name: Build Fuzzers (${{ matrix.sanitizer }})
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'litmuschaos'
|
||||
language: go
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
- name: Run Fuzzers (${{ matrix.sanitizer }})
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'litmuschaos'
|
||||
language: go
|
||||
fuzz-seconds: 120
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
output-sarif: true
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: ${{ matrix.sanitizer }}-artifacts
|
||||
path: ./out/artifacts
|
||||
- name: Upload Sarif
|
||||
if: always() && steps.build.outcome == 'success'
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
# Path to SARIF file relative to the root of the repository
|
||||
sarif_file: cifuzz-sarif/results.sarif
|
||||
checkout_path: cifuzz-sarif
|
|
@ -0,0 +1,71 @@
|
|||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, 0.9.x, 2.*, v* ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '21 2 * * 2'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go', 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
|
@ -2,14 +2,95 @@ name: Litmus-CI
|
|||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
if: contains(github.event.comment.html_url, '/pull/') && startsWith(github.event.comment.body, '/run')
|
||||
runs-on: ubuntu-20.04
|
||||
# Job for finding last commit sha of pull-request
|
||||
find-latest-commit-sha:
|
||||
if: contains(github.event.comment.html_url, '/pull/') && startsWith(github.event.comment.body, '/run-e2e')
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
commit-sha: ${{ steps.getcommit.outputs.sha }}
|
||||
steps:
|
||||
- uses: octokit/request-action@v2.x
|
||||
id: get_PR_commits
|
||||
with:
|
||||
route: GET /repos/${{ github.repository }}/pull_number/${{ github.event.issue.number }}/commits
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: set commit to output
|
||||
id: getcommit
|
||||
run: |
|
||||
prsha=$(echo $response | jq '.[-1].sha' | tr -d '"')
|
||||
echo "sha=$prsha" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
response: ${{ steps.get_PR_commits.outputs.data }}
|
||||
|
||||
find-changes:
|
||||
if: contains(github.event.comment.html_url, '/pull/') && startsWith(github.event.comment.body, '/run-e2e')
|
||||
runs-on: ubuntu-latest
|
||||
needs: find-latest-commit-sha
|
||||
# Set job outputs to values from filter step
|
||||
outputs:
|
||||
changed-paths: ${{ steps.filter.outputs.changes }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.find-latest-commit-sha.outputs.commit-sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
ref: ${{ needs.find-latest-commit-sha.outputs.commit-sha }}
|
||||
filters: |
|
||||
frontend:
|
||||
- 'litmus-portal/frontend/**'
|
||||
graphql-server:
|
||||
- 'litmus-portal/graphql-server/**'
|
||||
authentication:
|
||||
- 'litmus-portal/authentication/**'
|
||||
|
||||
docker-build-image:
|
||||
if: contains(github.event.comment.html_url, '/pull/') && startsWith(github.event.comment.body, '/run-e2e')
|
||||
runs-on: ubuntu-latest
|
||||
needs: [find-latest-commit-sha, find-changes]
|
||||
strategy:
|
||||
# Matrix of changed directories for building images in parallel
|
||||
matrix:
|
||||
path: ${{ fromJSON(needs.find-changes.outputs.changed-paths) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.find-latest-commit-sha.outputs.commit-sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Building Docker-Image for ${{ matrix.path }}
|
||||
run: |
|
||||
chmod 755 ./.github/filter_and_build.sh
|
||||
./.github/filter_and_build.sh
|
||||
env:
|
||||
directory: ${{ matrix.path }}
|
||||
|
||||
- name: upload docker artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Docker-Images
|
||||
path: Images
|
||||
|
||||
tests:
|
||||
if: contains(github.event.comment.html_url, '/pull/') && startsWith(github.event.comment.body, '/run-e2e')
|
||||
needs: [docker-build-image]
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Checkout litmus-E2E Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: litmuschaos/litmus-e2e
|
||||
path: litmus-e2e
|
||||
|
||||
- name: Notification for Starting Testing.
|
||||
uses: peter-evans/create-or-update-comment@v1
|
||||
|
@ -18,137 +99,81 @@ jobs:
|
|||
body: |
|
||||
****
|
||||
**Test Status:** The testing has been started please wait for the results ...
|
||||
#Using the last commit id of pull request
|
||||
- uses: octokit/request-action@v2.x
|
||||
id: get_PR_commits
|
||||
with:
|
||||
route: GET /repos/:repo/pulls/:pull_number/commits
|
||||
repo: ${{ github.repository }}
|
||||
pull_number: ${{ github.event.issue.number }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: set commit to output
|
||||
id: getcommit
|
||||
run: |
|
||||
prsha=$(echo $response | jq '.[-1].sha' | tr -d '"')
|
||||
echo "::set-output name=sha::$prsha"
|
||||
env:
|
||||
response: ${{ steps.get_PR_commits.outputs.data }}
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{steps.getcommit.outputs.sha}}
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: octokit/request-action@v2.x
|
||||
if: startsWith(github.event.comment.body, '/run-e2e')
|
||||
name: Getting the files changed in current Pull-Request
|
||||
id: get_files
|
||||
with:
|
||||
route: GET /repos/:repo/pulls/:pull_number/files
|
||||
repo: ${{ github.repository }}
|
||||
pull_number: ${{ github.event.issue.number }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setting up KinD Cluster
|
||||
if: startsWith(github.event.comment.body, '/run-e2e')
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
version: "v0.7.0"
|
||||
run: kind create cluster --wait 5m
|
||||
|
||||
- name: Configuring and Testing the Cluster Installation
|
||||
if: startsWith(github.event.comment.body, '/run-e2e')
|
||||
run: |
|
||||
kubectl cluster-info --context kind-kind
|
||||
kind get kubeconfig --internal >$HOME/.kube/config
|
||||
kubectl get nodes
|
||||
kubectl get pods -n kube-system
|
||||
|
||||
- name: Filtering the file paths and building images on the basic of changed files
|
||||
if: startsWith(github.event.comment.body, '/run-e2e')
|
||||
- name: download docker artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: Docker-Images
|
||||
path: Images
|
||||
|
||||
# Loading all build images of changed components in KIND Cluster
|
||||
- name: loading the docker build artifacts into KIND Cluster
|
||||
run: |
|
||||
chmod 755 ./.github/filter_and_build.sh
|
||||
./.github/filter_and_build.sh
|
||||
env:
|
||||
changed_data: ${{steps.get_files.outputs.data}}
|
||||
ls -1 Images/*.tar | xargs --no-run-if-empty -L 1 kind load image-archive
|
||||
|
||||
- name: Deploying the litmus-portal for E2E testing
|
||||
if: startsWith(github.event.comment.body, '/run-e2e')
|
||||
run: |
|
||||
wget https://raw.githubusercontent.com/litmuschaos/litmus/master/litmus-portal/cluster-k8s-manifest.yml
|
||||
sed -i 's/Always/IfNotPresent/g' cluster-k8s-manifest.yml
|
||||
kubectl apply -f cluster-k8s-manifest.yml
|
||||
kubectl get pods -n litmus
|
||||
kubectl get deployments -o wide -n litmus
|
||||
kubectl wait --for=condition=Ready pods --all --namespace litmus --timeout=120s
|
||||
|
||||
source ./litmus-e2e/litmus/utils.sh
|
||||
verify_all_components frontend,server litmus
|
||||
wait_for_pods litmus 720
|
||||
|
||||
- name: Getting the ENV variables for using while testing
|
||||
if: startsWith(github.event.comment.body, '/run-e2e')
|
||||
run: |
|
||||
export frontendPodName=$(kubectl get pods -l component=litmusportal-frontend --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' -n litmus)
|
||||
export frontendPodPort=$(kubectl get pod $frontendPodName --template='{{(index (index .spec.containers 0).ports 0).containerPort}}{{"\n"}}' --namespace litmus)
|
||||
kubectl port-forward $frontendPodName -n litmus 3001:$frontendPodPort &
|
||||
|
||||
- name: Setting Dependencies
|
||||
uses: actions/setup-node@v1
|
||||
if: startsWith(github.event.comment.body, '/run-unit')
|
||||
with:
|
||||
working-directory: litmus-portal/frontend
|
||||
export NODE_NAME=$(kubectl -n litmus get pod -l "component=litmusportal-frontend" -o=jsonpath='{.items[*].spec.nodeName}')
|
||||
export NODE_IP=$(kubectl -n litmus get nodes $NODE_NAME -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}')
|
||||
export NODE_PORT=$(kubectl -n litmus get -o jsonpath="{.spec.ports[0].nodePort}" services litmusportal-frontend-service)
|
||||
export AccessURL="http://$NODE_IP:$NODE_PORT"
|
||||
echo "URL=$AccessURL" >> $GITHUB_ENV
|
||||
|
||||
- name: Installing Dependencies for frontend.
|
||||
if: startsWith(github.event.comment.body, '/run-unit')
|
||||
run: |
|
||||
HUSKY_SKIP_INSTALL=1 npm i
|
||||
working-directory: litmus-portal/frontend
|
||||
|
||||
# Step for running all frontend Cypress unit tests.
|
||||
- name: Starting cypress unit tests
|
||||
if: startsWith(github.event.comment.body, '/run-unit')
|
||||
uses: cypress-io/github-action@v2
|
||||
continue-on-error: false
|
||||
with:
|
||||
command: npm run unit:ci
|
||||
install: false
|
||||
working-directory: litmus-portal/frontend
|
||||
|
||||
# Cloning the litmus-e2e repo for E2E tests.
|
||||
- name: Cloning the litmus-e2e Repo
|
||||
if: startsWith(github.event.comment.body, '/run-e2e')
|
||||
run: |
|
||||
git clone https://github.com/litmuschaos/litmus-e2e.git -b litmus-portal
|
||||
|
||||
- name: Running basic tests (Login and Welcome Modal Tests)
|
||||
if: startsWith(github.event.comment.body, '/run-e2e-AuthTests') || startsWith(github.event.comment.body, '/run-e2e')
|
||||
- name: Portal Authentication Tests
|
||||
if: always()
|
||||
uses: cypress-io/github-action@v2
|
||||
continue-on-error: false
|
||||
with:
|
||||
spec: cypress/integration/Basic_Setup/**/*.spec.js
|
||||
working-directory: litmus-e2e/CypressE2E/
|
||||
working-directory: litmus-e2e/Cypress/
|
||||
config-file: cypress.prod.json
|
||||
env: true
|
||||
env:
|
||||
CYPRESS_BASE_URL: ${{ env.URL }}
|
||||
|
||||
- name: Teaming and Account Settings Tests
|
||||
if: startsWith(github.event.comment.body, '/run-e2e-Settings')
|
||||
uses: cypress-io/github-action@v2
|
||||
continue-on-error: false
|
||||
with:
|
||||
spec: cypress/integration/Parallel_Tests/Account_Settings/*.spec.js
|
||||
working-directory: litmus-e2e/CypressE2E/
|
||||
config-file: cypress.prod.json
|
||||
- name: Verifying Execution Plane components
|
||||
if: always()
|
||||
run: |
|
||||
source ./litmus-e2e/litmus/utils.sh
|
||||
verify_all_components ${COMPONENTS} litmus
|
||||
wait_for_pods litmus 720
|
||||
env:
|
||||
COMPONENTS: subscriber,chaos-exporter,chaos-operator-ce,event-tracker,workflow-controller
|
||||
|
||||
- name: Run all E2E tests
|
||||
if: startsWith(github.event.comment.body, '/run-e2e-all')
|
||||
- name: Post Authentication Tests
|
||||
if: always()
|
||||
uses: cypress-io/github-action@v2
|
||||
continue-on-error: false
|
||||
with:
|
||||
spec: cypress/integration/Parallel_Tests/**/*.spec.js
|
||||
working-directory: litmus-e2e/CypressE2E
|
||||
working-directory: litmus-e2e/Cypress/
|
||||
config-file: cypress.prod.json
|
||||
env: true
|
||||
env:
|
||||
CYPRESS_BASE_URL: ${{ env.URL }}
|
||||
|
||||
- name: Check the test run
|
||||
if: |
|
||||
startsWith(github.event.comment.body, '/run-unit') || startsWith(github.event.comment.body, '/run-e2e')
|
||||
if: always()
|
||||
run: |
|
||||
echo "TEST_RUN=true" >> $GITHUB_ENV
|
||||
|
||||
|
@ -189,15 +214,5 @@ jobs:
|
|||
RUN_ID: ${{ github.run_id }}
|
||||
|
||||
- name: Deleting KinD cluster
|
||||
if: startsWith(github.event.comment.body, '/run-e2e')
|
||||
if: always()
|
||||
run: kind delete cluster
|
||||
|
||||
merge:
|
||||
if: contains(github.event.comment.html_url, '/pull/') && startsWith(github.event.comment.body, '/merge')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add a merge label when all jobs are passed
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: merge
|
|
@ -0,0 +1,27 @@
|
|||
name: Deploy Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: build docs
|
||||
run: |
|
||||
cd mkdocs
|
||||
pip install mkdocs mkdocs_material
|
||||
mkdocs build
|
||||
- name: deploy docs
|
||||
uses: peaceiris/actions-gh-pages@v2.5.0
|
||||
env:
|
||||
PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||
PUBLISH_BRANCH: gh-pages
|
||||
PUBLISH_DIR: ./mkdocs/site
|
|
@ -3,37 +3,43 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- master
|
||||
- v*
|
||||
- ^v[0-9]*.[0-9]*.x$
|
||||
tags:
|
||||
- "*"
|
||||
defaults:
|
||||
run:
|
||||
working-directory: chaoscenter
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
backend-checks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- shell: bash
|
||||
run: |
|
||||
cd litmus-portal
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.22" # By default, the go version is v1.15 in runner.
|
||||
- run: |
|
||||
make backend-services-checks
|
||||
|
||||
frontend-checks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- shell: bash
|
||||
run: |
|
||||
cd litmus-portal
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
- run: |
|
||||
make frontend-services-checks
|
||||
|
||||
get-envs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- shell: bash
|
||||
run: |
|
||||
uses: actions/checkout@v4
|
||||
- run: |
|
||||
img_tag=""
|
||||
array=(`echo ${GITHUB_REF} | sed 's/\//\n/g'`)
|
||||
if [ ${array[1]} == "tags" ]
|
||||
|
@ -53,11 +59,13 @@ jobs:
|
|||
echo export FRONTEND_IMAGE="litmusportal-frontend" >> env-vars
|
||||
echo export SUBSCRIBER_IMAGE="litmusportal-subscriber" >> env-vars
|
||||
echo export EVENT_TRACKER="litmusportal-event-tracker" >> env-vars
|
||||
echo export DEX_SERVER="litmusportal-dex-server" >> env-vars
|
||||
|
||||
- name: Uploading envs
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
path: env-vars
|
||||
path: chaoscenter/env-vars
|
||||
|
||||
docker-build-and-push-graphql-server:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -66,32 +74,36 @@ jobs:
|
|||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
- name: Build graphql server docker image
|
||||
shell: bash
|
||||
run: |
|
||||
source env-vars
|
||||
cd litmus-portal/graphql-server
|
||||
docker build . -f build/Dockerfile -t ${{ secrets.REPONAME }}/${GRAPHQL_SERVER_IMAGE}:${IMG_TAG}
|
||||
path: chaoscenter
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push graphql server docker image
|
||||
shell: bash
|
||||
env:
|
||||
IMAGE_NAME: ${GRAPHQL_SERVER_IMAGE}
|
||||
IMG_TAG: ${IMG_TAG}
|
||||
PLATFORMS: ${{ secrets.PLATFORMS }}
|
||||
REPONAME: ${{ secrets.REPONAME }}
|
||||
DIRECTORY: "graphql/server"
|
||||
run: |
|
||||
branch=${GITHUB_REF#refs/*/}
|
||||
array=(`echo ${GITHUB_REF} | sed 's/\//\n/g'`)
|
||||
if [ $branch == "master" ] || [ ${array[1]} == "tags" ] || [[ $branch =~ ^v[0-9]*.[0-9]*.x$ ]]
|
||||
then
|
||||
source env-vars
|
||||
docker push ${{ secrets.REPONAME }}/${GRAPHQL_SERVER_IMAGE}:${IMG_TAG}
|
||||
fi
|
||||
source env-vars
|
||||
make push-portal-component
|
||||
|
||||
docker-build-and-push-authentication-server:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -100,32 +112,36 @@ jobs:
|
|||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
- name: Build auth server docker image
|
||||
shell: bash
|
||||
run: |
|
||||
source env-vars
|
||||
cd litmus-portal/authentication
|
||||
docker images && docker build . -f Dockerfile -t ${{ secrets.REPONAME }}/${AUTHENTICATION_SERVER_IMAGE}:${IMG_TAG}
|
||||
path: chaoscenter
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push auth server docker image
|
||||
shell: bash
|
||||
env:
|
||||
IMAGE_NAME: ${AUTHENTICATION_SERVER_IMAGE}
|
||||
IMG_TAG: ${IMG_TAG}
|
||||
PLATFORMS: ${{ secrets.PLATFORMS }}
|
||||
REPONAME: ${{ secrets.REPONAME }}
|
||||
DIRECTORY: "authentication"
|
||||
run: |
|
||||
branch=${GITHUB_REF#refs/*/}
|
||||
array=(`echo ${GITHUB_REF} | sed 's/\//\n/g'`)
|
||||
if [ $branch == "master" ] || [ ${array[1]} == "tags" ] || [[ $branch =~ ^v[0-9]*.[0-9]*.x$ ]]
|
||||
then
|
||||
source env-vars
|
||||
docker push ${{ secrets.REPONAME }}/${AUTHENTICATION_SERVER_IMAGE}:${IMG_TAG}
|
||||
fi
|
||||
source env-vars
|
||||
make push-portal-component
|
||||
|
||||
docker-build-and-push-subscriber:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -134,32 +150,36 @@ jobs:
|
|||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
- name: Build subscriber docker image
|
||||
shell: bash
|
||||
run: |
|
||||
source env-vars
|
||||
cd litmus-portal/cluster-agents/subscriber
|
||||
docker build . -f build/Dockerfile -t ${{ secrets.REPONAME }}/${SUBSCRIBER_IMAGE}:${IMG_TAG}
|
||||
path: chaoscenter
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push subscriber docker image
|
||||
shell: bash
|
||||
env:
|
||||
IMAGE_NAME: ${SUBSCRIBER_IMAGE}
|
||||
IMG_TAG: ${IMG_TAG}
|
||||
PLATFORMS: ${{ secrets.PLATFORMS }}
|
||||
REPONAME: ${{ secrets.REPONAME }}
|
||||
DIRECTORY: "subscriber"
|
||||
run: |
|
||||
branch=${GITHUB_REF#refs/*/}
|
||||
array=(`echo ${GITHUB_REF} | sed 's/\//\n/g'`)
|
||||
if [ $branch == "master" ] || [ ${array[1]} == "tags" ] || [[ $branch =~ ^v[0-9]*.[0-9]*.x$ ]]
|
||||
then
|
||||
source env-vars
|
||||
docker push ${{ secrets.REPONAME }}/${SUBSCRIBER_IMAGE}:${IMG_TAG}
|
||||
fi
|
||||
source env-vars
|
||||
make push-portal-component
|
||||
|
||||
docker-build-and-push-event-tracker:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -168,64 +188,133 @@ jobs:
|
|||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
- name: Build event tracker docker image
|
||||
shell: bash
|
||||
run: |
|
||||
source env-vars
|
||||
cd litmus-portal/cluster-agents/event-tracker
|
||||
docker build . -f Dockerfile -t ${{ secrets.REPONAME }}/${EVENT_TRACKER}:${IMG_TAG}
|
||||
path: chaoscenter
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Push event tracker docker image
|
||||
shell: bash
|
||||
run: |
|
||||
branch=${GITHUB_REF#refs/*/}
|
||||
array=(`echo ${GITHUB_REF} | sed 's/\//\n/g'`)
|
||||
if [ $branch == "master" ] || [ ${array[1]} == "tags" ] || [[ $branch =~ ^v[0-9]*.[0-9]*.x$ ]]
|
||||
then
|
||||
source env-vars
|
||||
docker push ${{ secrets.REPONAME }}/${EVENT_TRACKER}:${IMG_TAG}
|
||||
fi
|
||||
|
||||
- name: Push event tracker docker image
|
||||
env:
|
||||
IMAGE_NAME: ${EVENT_TRACKER}
|
||||
IMG_TAG: ${IMG_TAG}
|
||||
PLATFORMS: ${{ secrets.PLATFORMS }}
|
||||
REPONAME: ${{ secrets.REPONAME }}
|
||||
DIRECTORY: "event-tracker"
|
||||
run: |
|
||||
source env-vars
|
||||
make push-portal-component
|
||||
|
||||
## TODO: the build time for multiarch frontend image is higher so it is used in the release
|
||||
## builds only need to optimize the build duration and also add it with ci tag
|
||||
## Build and push / base path
|
||||
docker-build-and-push-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
frontend:
|
||||
[
|
||||
{ "image_name": "litmusportal-frontend", "path": "" },
|
||||
{
|
||||
"image_name": "litmusportal-frontend-path-litmuschaos",
|
||||
"path": "/litmuschaos",
|
||||
},
|
||||
]
|
||||
needs:
|
||||
- get-envs
|
||||
- frontend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
- name: Build frontend docker image
|
||||
shell: bash
|
||||
path: chaoscenter
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: yarn build
|
||||
run: |
|
||||
source env-vars
|
||||
cd litmus-portal/frontend
|
||||
timestamp=`date "+%s"`
|
||||
docker build . -f Dockerfile -t ${{ secrets.REPONAME }}/${FRONTEND_IMAGE}:${IMG_TAG} --build-arg REACT_APP_KB_CHAOS_VERSION=${IMG_TAG} --build-arg REACT_APP_BUILD_TIME="$timestamp" --build-arg REACT_APP_HUB_BRANCH_NAME="v1.13.x"
|
||||
cd web && yarn && yarn build
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Push frontend docker image
|
||||
shell: bash
|
||||
|
||||
- name: Push frontend docker image ( ${{ matrix.frontend.image_name }} )
|
||||
env:
|
||||
IMG_TAG: ${IMG_TAG}
|
||||
PLATFORMS: ${{ secrets.PLATFORMS }}
|
||||
REPONAME: ${{ secrets.REPONAME }}
|
||||
timestamp: ${timestamp}
|
||||
PUBLIC_URL: ${{ matrix.frontend.path }}
|
||||
run: |
|
||||
branch=${GITHUB_REF#refs/*/}
|
||||
array=(`echo ${GITHUB_REF} | sed 's/\//\n/g'`)
|
||||
if [ $branch == "master" ] || [ ${array[1]} == "tags" ] || [[ $branch =~ ^v[0-9]*.[0-9]*.x$ ]]
|
||||
then
|
||||
source env-vars
|
||||
docker push ${{ secrets.REPONAME }}/${FRONTEND_IMAGE}:${IMG_TAG}
|
||||
fi
|
||||
source env-vars
|
||||
FRONTEND_IMAGE=${{ matrix.frontend.image_name }}
|
||||
timestamp=`date "+%s"`
|
||||
make push-frontend
|
||||
|
||||
|
||||
docker-build-and-push-dex-server:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- get-envs
|
||||
- backend-checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Downloading image artficate
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: env_artifact
|
||||
path: chaoscenter
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push dex server docker image
|
||||
env:
|
||||
IMAGE_NAME: ${DEX_SERVER}
|
||||
IMG_TAG: ${IMG_TAG}
|
||||
PLATFORMS: ${{ secrets.PLATFORMS }}
|
||||
REPONAME: ${{ secrets.REPONAME }}
|
||||
DIRECTORY: "dex-server"
|
||||
run: |
|
||||
source env-vars
|
||||
make push-portal-component
|
||||
|
|
|
@ -17,4 +17,7 @@ _vendor-*
|
|||
tmp/
|
||||
.env
|
||||
litmus-portal-service
|
||||
litmus-portal-app
|
||||
litmus-portal-app
|
||||
litmus-portal/graphql-server/server
|
||||
|
||||
**/client_id
|
|
@ -1,53 +0,0 @@
|
|||
---
|
||||
stages:
|
||||
- litmusImageBuild
|
||||
- litmusPythonTest
|
||||
- litmusAnsibleTest
|
||||
- baseline
|
||||
- litmusImagePush
|
||||
|
||||
Build Litmus Image:
|
||||
stage: litmusImageBuild
|
||||
script:
|
||||
- sudo docker build . -f build/ansible-runner/Dockerfile -t litmuschaos/ansible-runner:ci
|
||||
|
||||
Test Litmus PyTest Suite:
|
||||
stage: litmusPythonTest
|
||||
script:
|
||||
- ./hack/pytest-suite
|
||||
|
||||
Test Litmus Playbook Syntax:
|
||||
stage: litmusAnsibleTest
|
||||
script:
|
||||
- sudo docker run litmuschaos/ansible-runner:ci ./syntax-check
|
||||
|
||||
baseline-image:
|
||||
|
||||
when: always
|
||||
stage: baseline
|
||||
tags:
|
||||
- test
|
||||
only:
|
||||
refs:
|
||||
- /^(v[0-9][.][0-9][.]x|master)?$/
|
||||
script:
|
||||
- pwd
|
||||
- export BRANCH=${CI_COMMIT_REF_NAME}
|
||||
- echo $BRANCH
|
||||
- export COMMIT=${CI_COMMIT_SHORT_SHA}
|
||||
- echo $COMMIT
|
||||
- git clone https://github.com/litmuschaos/litmus-e2e.git
|
||||
- cd litmus-e2e
|
||||
- git checkout ${BASELINE_BRANCH}
|
||||
- cd baseline
|
||||
- ansible-playbook commit-writer.yml --extra-vars "branch=$BRANCH repo=$CI_PROJECT_NAME commit=$COMMIT"
|
||||
- git status
|
||||
- git add baseline
|
||||
- git status
|
||||
- git commit -m "updated $CI_PROJECT_NAME commit:$COMMIT"
|
||||
- git push http://${YOUR_USERNAME}:${PERSONAL_ACCESS_TOKEN}@github.com/litmuschaos/litmus-e2e.git --all
|
||||
|
||||
Push Litmus Image:
|
||||
stage: litmusImagePush
|
||||
script:
|
||||
- REPONAME="litmuschaos" IMGNAME="ansible-runner" IMGTAG="ci" ./hack/push
|
|
@ -0,0 +1,17 @@
|
|||
57748d7e930664094c76ae6b02577db2ce3955fa:mkdocs/docs/auth/v3.0.0/auth-api.json:jwt:140
|
||||
57748d7e930664094c76ae6b02577db2ce3955fa:mkdocs/docs/auth/v3.0.0/auth-api.json:jwt:185
|
||||
57748d7e930664094c76ae6b02577db2ce3955fa:mkdocs/docs/auth/v3.0.0/auth-api.json:jwt:278
|
||||
28f9079f4a1909f277a47f0d45be1f82a84de27d:mkdocs/docs/auth/v3.0.0/auth-api.json:jwt:46
|
||||
cd1841bb0d83decc0ab0f09ddda69cc572c50da9:auth/api.html:jwt:293
|
||||
cd1841bb0d83decc0ab0f09ddda69cc572c50da9:auth/swagger.yml:jwt:56
|
||||
512b7bbc16d1f8198872fb517814362b81b18388:docs/auth/swagger.yml:jwt:56
|
||||
512b7bbc16d1f8198872fb517814362b81b18388:docs/auth/api.html:jwt:293
|
||||
32e35ad4fb9c7c2ba8ed4a6043176f1e73801814:monitoring/utils/sample-application-under-test/percona/cr.yaml:hashicorp-tf-password:213
|
||||
352f8c6ae9a3b93ce841e6bc3b3a296c0f917846:litmus-portal/backend/auth/pkg/providers/github/github.go:generic-api-key:14
|
||||
92da50bb8ff07fce3427e70449844b77b7ee3270:apps/datastax/deployers/common/opscenter/conf-dir/conf/ssl/opscenter.key:private-key:1
|
||||
edb1d8dc6e2d1044a9fcb3677727363bbe1e267c:mysql-master/Dockerfile:generic-api-key:37
|
||||
edb1d8dc6e2d1044a9fcb3677727363bbe1e267c:mysql-slave/Dockerfile:generic-api-key:37
|
||||
fd88a74c81ae34796a42fafed1a61bf1edc06607:auth/v3.0.0/auth-api.json:jwt:140
|
||||
fd88a74c81ae34796a42fafed1a61bf1edc06607:auth/v3.0.0/auth-api.json:jwt:185
|
||||
fd88a74c81ae34796a42fafed1a61bf1edc06607:auth/v3.0.0/auth-api.json:jwt:278
|
||||
6eb10ad366fb3bd040cda61e8c64e47f15d576be:auth/v3.0.0/auth-api.json:jwt:46
|
|
@ -0,0 +1,2 @@
|
|||
# Accept the risk (RedHat)
|
||||
CVE-2024-2961
|
114
ADOPTERS.md
114
ADOPTERS.md
|
@ -1,31 +1,87 @@
|
|||
This is the list of organizations and users that publicly shared details of how they are using LitmusChaos for running chaos experiments.
|
||||
Please send PRs to add or remove organizations/users.
|
||||
This is a list of organizations that have publicly acknowledged usage of LitmusChaos and shared details of how they are leveraging it for chaos engineering.
|
||||
Please send a PR to this file (along with details in a respective [org](./adopters/organizations) folder) to add/remove entries. If you are an independent user
|
||||
and wish to to share your adoption story, please raise a PR against the [users](USERS.md) file.
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :--- | :--- | :--- |
|
||||
|[Zebrium](https://www.zebrium.com?utm_source=github&utm_campaign=litmuschaos_repo)|[Zebrium K8s Chaos Project](https://github.com/zebrium/zebrium-kubernetes-demo)|[Our Story](adopters/organizations/zebrium.md)|
|
||||
|[MayaData](https://mayadata.io)|[Director Online](https://director.mayadata.io/)|[Our Story](adopters/organizations/mayadata.md)|
|
||||
|[OpenEBS](https://openebs.io/)|[Openebs-CI](https://openebs.ci/)|[Our Story](adopters/organizations/openebs.md)|
|
||||
|[Wipro](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo)|[Wipro AppAnywhere](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo)|[Our Story](adopters/organizations/wipro.md)|
|
||||
|[Intuit](https://www.intuit.com?utm_source=github&utm_campaign=litmuschaos_repo)|[Argo Based Chaos Workflows](https://youtu.be/Uwqop-s99LA?t=720)|[Our Story](adopters/organizations/intuit.md)|
|
||||
|[Okteto](https://okteto.com)|[Okteto-Litmus Integration](https://okteto.com/blog/chaos-engineering-with-litmus/)| [Our Story](adopters/organizations/okteto.md)|
|
||||
|[WeScale](https://www.wescale.fr)|[Chaos Engineering](https://blog.wescale.fr/2020/03/19/le-guide-de-chaos-engineering-partie-2/)|[Our Story](adopters/organizations/wescale.md)|
|
||||
|[NetApp](https://www.netapp.com)|[Chaos Engineering](https://www.netapp.com/us/index.aspx)|[Our Story](adopters/organizations/netapp.md)|
|
||||
|[Keptn](https://keptn.sh)|[Chaos Engineering integration in CD](https://www.youtube.com/watch?v=aa5SzQmv4EQ)|To Be Added|
|
||||
|[AkriData](https://www.akridata.com/)|Pod Chaos Experiments in AWS & Azure|[Our Story](adopters/organizations/akridata.md)|
|
||||
|[Raspbernetes](https://github.com/raspbernetes)|Resilience of OpenEBS & other apps on ARM64 based clusters|[Our Story](adopters/organizations/raspbernetes.md)|
|
||||
|[Kublr](https://kublr.com/)|Identify the weak spots and components prone to failures under stress|[Our Story](adopters/organizations/kublr.md)|
|
||||
|[Orange](https://www.orange.com)|[Cloud Infra Resiliency](https://youtu.be/UOhjFbCrncw?list=PLBuYBMjBLBzHPuPsvdbJvKu1KxSowWDYl&t=186...a)|[Our Story](adopters/organizations/orange.md)|
|
||||
|[RedHat](https://www.redhat.com/en)|[RedHat Openshift Virtualization Maturity](https://www.youtube.com/watch?v=VITGHJ47gx8&list=PLBuYBMjBLBzHPuPsvdbJvKu1KxSowWDYl&index=7)|[Our Story](adopters/organizations/redhat.md)|
|
||||
|[VMWare](https://www.vmware.com/)|Chaos Engineering in CD|[Our Story](adopters/organizations/vmware.md)|
|
||||
|[Kitopi](https://www.kitopi.com/)|Chaos Engineering as part of SRE practice|[Our Story](adopters/organizations/kitopi.md)|
|
||||
|[AnutaNetworks](https://www.anutanetworks.com/)|Chaos Engineering as part of SRE practices in QA environments |[Our Story](adopters/organizations/anutanetworks.md)|
|
||||
These organizations have been broadly classified on the basis of how they contribute to the ecosystem: as vendors, as solution providers or as pure end-users of
|
||||
cloud-native technologies. Also included in this list are CNCF (or other) open-source projects that have integrated with Litmus or use it as part of their release/delivery process.
|
||||
|
||||
| User | Usecase | Details |
|
||||
| :--- | :--- | :--- |
|
||||
| [Laura Henning](https://github.com/LaumiH)|Reasearch on how to do chaos engineering in minikube clusters like [these](https://github.com/LaumiH/k8sstuff)|[My Story](adopters/users/Laura_Henning.md) |
|
||||
| [Johnny Jacob](https://github.com/johnnyjacob)|Testing deployment designs for resiliency|Coming Soon!|
|
||||
| [Jayesh Kumar Tank](https://github.com/k8s-dev)|Create Cloud Native Validation Suite on [Microservices Application](https://github.com/k8s-dev/microservices-demo)|[My Story](adopters/users/Jayesh_Kumar_Tank.md)|
|
||||
| [Bhaumik Shah](https://github.com/Bhaumik1802)|Use LitmusChaos for Kafka Resiliency on Dev/Staging|[My Story](adopters/users/Bhaumik_Shah.md)|
|
||||
| [Jayadeep KM](https://github.com/kmjayadeep)|Ensure reliability of microservices|[My Story](adopters/users/Jayadeep_KM.md)|
|
||||
| [Shantanu Deshpande](https://github.com/ishantanu)|Chaos Engineering Practice as SRE|[My Story](adopters/users/Shantanu_Deshpande.md)|
|
||||
### Cloud-Native End Users
|
||||
|
||||
The companies listed here conform to [CNCF's definition of end-users](https://github.com/cncf/enduser-public#cncf-end-user-community).
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- |
|
||||
| [AnutaNetworks](https://www.anutanetworks.com/) | Chaos Engineering as part of SRE practices in QA environments | [Our Story](adopters/organizations/anutanetworks.md) |
|
||||
| [AkriData](https://www.akridata.com/) | Pod Chaos Experiments in AWS & Azure | [Our Story](adopters/organizations/akridata.md) |
|
||||
| [Halodoc](https://www.halodoc.com/) | Resiliency Validation of Kubernetes Workloads and Infra on AWS | [Our Story](adopters/organizations/halodoc.md) |
|
||||
| [Intuit](https://www.intuit.com?utm_source=github&utm_campaign=litmuschaos_repo) | [Argo Based Chaos Workflows](https://youtu.be/Uwqop-s99LA?t=720) | [Our Story](adopters/organizations/intuit.md) |
|
||||
| [Kitopi](https://www.kitopi.com/) | Chaos Engineering as part of SRE practice | [Our Story](adopters/organizations/kitopi.md) |
|
||||
| [Lenskart](https://www.lenskart.com/) | Chaos Engineering for better Resiliency | [Our Story](adopters/organizations/lenskart.md) |
|
||||
| [Mercedes](https://www.mercedes-benz.com/) | Resiliency validation for applications | [Our Story](adopters/organizations/mercedes.md) |
|
||||
| [Orange](https://www.orange.com) | [Cloud Infra Resiliency](https://youtu.be/UOhjFbCrncw?list=PLBuYBMjBLBzHPuPsvdbJvKu1KxSowWDYl&t=186...a) | [Our Story](adopters/organizations/orange.md) |
|
||||
| [Pôle Emploi](https://www.pole-emploi.fr) | Chaos Engineering as part of SRE practice | [Our Story](adopters/organizations/pole_emploi.md) |
|
||||
| [iFood](https://www.ifood.com.br/) | Chaos Engineering for a Food Delivery Platform | [Our Story](adopters/organizations/ifood.md) |
|
||||
| [FIS](https://www.fisglobal.com/en/) | Larger SRE Transformation with Chaos Engineering | [Our Story](adopters/organizations/fis.md) |
|
||||
| [Adidas](https://adidas.com/) | Implementing Chaos Engineering as a practice at Adidas | [Our Story](adopters/organizations/adidas.md) |
|
||||
| [Cyren](https://www.cyren.com/) | Implementing Chaos Engineering as a practice at Cyren | [Our Story](https://www.infoq.com/articles/chaos-engineering-cloud-native/) |
|
||||
| [AB-Inbev](https://www.ab-inbev.com/) | Implementing Chaos Engineering as a practice at AB-Inbev | [Our Story](adopters/organizations/abinbev.md) |
|
||||
| [Group Baobab](https://baobab.com/en/home/) | Orchestrating Chaos using LitmusChaos at Baobab | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-1647648343) |
|
||||
| [Flipkart](https://www.flipkart.com/) | Chaos Engineering at Flipkart | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-1966904935) |
|
||||
| [Talend](https://www.talend.com/) | Chaos Engineering for our pipelines and weekly checks | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-2005254600) |
|
||||
| [Delivery Hero](https://www.deliveryhero.com/) | Enhancing Resiliency of Our Services | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-1997465958) |
|
||||
| [Wingie Enuygun Company](https://www.wingie.com/) | Chaos Engineering for an Online Travel and Finance Platform | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-2331265698) |
|
||||
| [EmiratesNBD](https://www.emiratesnbd.com) | Chaos Engineering for Government Owned Bank | [Our Story](adopters/organizations/emirates-nbd.md) |
|
||||
| [PokerBaazi](https://www.pokerbaazi.com/) | Chaos Engineering for Online Gaming | [Our Story](adopters/organizations/pokerbaazi.md) |
|
||||
| [Amadeus](https://amadeus.com/) | Enhance the resilience and reliability in Amadeus through Chaos Engineering | [Our Story](adopters/organizations/amadeus.md) |
|
||||
|
||||
### Cloud-Native Vendors
|
||||
|
||||
The companies listed here sell cloud-native products/technologies. They use LitmusChaos as part of the resiliency validation of these products OR as part of other
|
||||
devops/reliability pipelines (such as for customer portals/websites etc.,) within the company.
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :--------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------- |
|
||||
| [KubeSphere](https://kubesphere.io/) | Chaos Engineering | To Be Added |
|
||||
| [Kublr](https://kublr.com/) | Identify the weak spots and components prone to failures under stress | [Our Story](adopters/organizations/kublr.md) |
|
||||
| [MayaData](https://mayadata.io) | [Director Online](https://director.mayadata.io/) | [Our Story](adopters/organizations/mayadata.md) |
|
||||
| [NetApp](https://www.netapp.com) | [Chaos Engineering](https://www.netapp.com/us/index.aspx) | [Our Story](adopters/organizations/netapp.md) |
|
||||
| [Okteto](https://okteto.com) | [Okteto-Litmus Integration](https://okteto.com/blog/chaos-engineering-with-litmus/) | [Our Story](adopters/organizations/okteto.md) |
|
||||
| [RedHat](https://www.redhat.com/en) | [RedHat Openshift Virtualization Maturity](https://www.youtube.com/watch?v=VITGHJ47gx8&list=PLBuYBMjBLBzHPuPsvdbJvKu1KxSowWDYl&index=7) | [Our Story](adopters/organizations/redhat.md) |
|
||||
| [VMWare](https://www.vmware.com/) | Chaos Engineering in CD | [Our Story](adopters/organizations/vmware.md) |
|
||||
| [Zebrium](https://www.zebrium.com?utm_source=github&utm_campaign=litmuschaos_repo) | [Zebrium K8s Chaos Project](https://github.com/zebrium/zebrium-kubernetes-demo) | [Our Story](adopters/organizations/zebrium.md) |
|
||||
| [Container Solutions](https://www.container-solutions.com/) | Building Chaos Engineering for E-Commerce Customers | [Our Story](adopters/organizations/containersolutions.md) |
|
||||
| [Infracloud Technologies](https://www.infracloud.io/) | Developing Resiliency Framework at Infracloud | [Our Story](adopters/organizations/infracloud.md) |
|
||||
| [IFS](https://www.ifs.com/) | Checking Resiliency with LitmusChaos at IFS | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-1966428068) |
|
||||
| [Ericsson](https://www.ericsson.com/en) | Chaos Engineering with Open Source LitmusChaos | [Our Story](https://github.com/litmuschaos/litmus/issues/2191#issuecomment-1985348431) |
|
||||
| [OutSystems](https://www.outsystems.com/) | Chaos Engineering for Low-Code Platform | [Our Story](adopters/organizations/outsystems.md) |
|
||||
|
||||
### Cloud-Native Solutions & Service Providers
|
||||
|
||||
The companies listed here provide solutions around cloud-native technologies to other organizations/clients and are often involved in their implementation/offer services.
|
||||
They use LitmusChaos as the tool of choice for carrying out chaos experiments in a client environment or in some cases use it as a building block of a larger bespoke software/devops platform.
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :---------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------- |
|
||||
| [Klanik](https://www.klanik.com) | Chaos Engineering as part of SRE practice | [Our Story](adopters/organizations/klanik.md) |
|
||||
| [Neudesic](https://www.neudesic.com/) | Chaos Engineering | [Our Story](adopters/organizations/neudesic.md) |
|
||||
| [WeScale](https://www.wescale.fr) | [Chaos Engineering](https://blog.wescale.fr/2020/03/19/le-guide-de-chaos-engineering-partie-2/) | [Our Story](adopters/organizations/wescale.md) |
|
||||
| [Wipro](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo) | [Wipro AppAnywhere](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo) | [Our Story](adopters/organizations/wipro.md) |
|
||||
| [HCL Cloud Native Labs](https://www.hcltech.com/) | SRE Enablement Service | [Our Story(TBA)] |
|
||||
| [CI&T](https://ciandt.com/us/en-us) | Chaos Engineering Implementation | [Our Story](adopters/organizations/ci&t.md) |
|
||||
|
||||
### Cloud-Native OSS Projects
|
||||
|
||||
The projects listed here, in most cases use LitmusChaos for testing the resilience of the respective opensource framework/platform
|
||||
(in a manual or automated fashion) or in other cases integrate with it via a plugin/service to provide add resilience validation capability to their
|
||||
existing functions.
|
||||
|
||||
| Organization | Usecase | Details |
|
||||
| :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Keptn](https://keptn.sh) | [Chaos Engineering integration in CD](https://www.youtube.com/watch?v=aa5SzQmv4EQ) | [Our Story](https://medium.com/keptn/part-2-evaluating-application-resiliency-with-keptn-and-litmuschaos-use-case-and-demo-f43b264a2294) |
|
||||
| [KubeFlare](https://github.com/raspbernetes) | Resilience of microservices on ARM64 (Raspberry Pi) based clusters | [Our Story](adopters/organizations/raspbernetes.md) |
|
||||
| [OpenEBS](https://openebs.io/) | [Openebs-CI](https://openebs.ci/) | [Our Story](adopters/organizations/openebs.md) |
|
||||
| [Pravega](https://pravega.io/) | To inject faults while exercising quality tests on our product | [Our Story](adopters/organizations/pravega.md) |
|
||||
| [Red Hat](https://www.redhat.com/en) | [Chaos Engineering with Kraken](https://github.com/cloud-bulldozer/kraken) | [Our Story](adopters/organizations/redhat_kraken.md) |
|
||||
| [Iter8](https://iter8.tools) | [SLO validation with chaos injection](https://iter8.tools/0.7/tutorials/deployments/slo-validation-chaos/) | To Be Added |
|
||||
| [CNF Test Suite](https://github.com/cncf/cnf-testsuite) | To validate the resilience of Cloud Native Network Functions (CNFs) | [Our Story](adopters/organizations/cnftestsuite.md) |
|
||||
| [APACHE APISIX](https://apisix.apache.org/) | Practicing Chaos Engineering using Litmus in the Apache APISIX Ingress. | [Our Story](adopters/organizations/apisix.md) |
|
||||
|
|
|
@ -2,45 +2,131 @@
|
|||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official email address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing or otherwise unacceptable behavior may be reported by contacting the project team at support@mayadata.io. The project team will review and investigate all complaints, and will respond in a way that it seems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
sayan.mondal@harness.io.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# LitmusChaos Commercial Support Providers
|
||||
|
||||
Here is a list of third-party companies and individuals who provide products or services related to LitmusChaos. LitmusChaos is a CNCF project which does not endorse any company.
|
||||
If you are a commercial support provider for LitmusChaos and wish to add your company, please raise a PR against this document.
|
||||
|
||||
The list is provided in alphabetical order.
|
||||
|
||||
- [Harness](https://harness.io/products/chaos-engineering)
|
123
CONTRIBUTING.md
123
CONTRIBUTING.md
|
@ -1,56 +1,109 @@
|
|||
<img src="https://avatars0.githubusercontent.com/u/49853472?s=200&v=4">
|
||||
|
||||
# Contributing to Litmus :
|
||||
# Contributing to Litmus
|
||||
|
||||
Litmus is an Apache 2.0 Licensed project and uses the standard GitHub pull requests process to review and accept contributions.
|
||||
---
|
||||
|
||||
There are several areas of Litmus that could use your help. For starters, you could help in improving the sections in this document by either creating a new issue describing the improvement or submitting a pull request to this repository.
|
||||
Thanks for your interest in contributing to Litmus and help improve the project! ⚡️✨
|
||||
|
||||
* If you are a first-time contributor, please see [Steps to Contribute](#steps-to-contribute).
|
||||
* If you would like to suggest new tests to be added to litmus, please go ahead and [create a new issue](https://github.com/litmuschaos/litmus/issues/new) describing your test. All you need to do is specify the workload type and the operations that you would like to perform on the workload.
|
||||
* If you would like to work on something more involved, please connect with the Litmus Contributors.
|
||||
* If you would like to make code contributions, all your commits should be signed with Developer Certificate of Origin. See [Sign your work](#sign-your-work).
|
||||
## Where to Begin!
|
||||
|
||||
## Steps to Contribute :
|
||||
If you have any queries or requests about Litmus please [create an issue](https://github.com/litmuschaos/litmus/issues/new) on GitHub. If you want to comment or ask questions to the contributors start by [joining our community](http://slack.litmuschaos.io) and drop your questions in the **#litmus** channel.
|
||||
|
||||
* Find an issue to work on or create a new issue. The issues are maintained at [litmuschaos/litmus](https://github.com/litmuschaos/litmus/issues). You can pick up from a list of [good-first-issues](https://github.com/litmuschaos/litmus/labels/good%20first%20issue).
|
||||
* Claim your issue by commenting your intent to work on it to avoid duplication of efforts.
|
||||
* Fork the repository on GitHub.
|
||||
* Create a branch from where you want to base your work (usually master).
|
||||
* Make your changes.
|
||||
* Relevant coding style guidelines are the [Go Code Review Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) and the _Formatting and style_ section of Peter Bourgon's [Go: Best Practices for Production Environments](https://peter.bourgon.org/go-in-production/#formatting-and-style).
|
||||
* Commit your changes by making sure the commit messages convey the need and notes about the commit.
|
||||
* Push your changes to the branch in your fork of the repository.
|
||||
* Submit a pull request to the original repository. See [Pull Request checklist](#pull-request-checklist)
|
||||
If you want to do code contributions but you are fairly new to the tech stack we are using! Check out the [Local Development Guide](https://github.com/litmuschaos/litmus/wiki/ChaosCenter-Development-Guide) and [Development Best Practices](https://github.com/litmuschaos/litmus/wiki/Development-Best-Practices) to get a reference and help get started.
|
||||
|
||||
We welcome contributions of all kinds
|
||||
|
||||
- Development of features, bug fixes, and other improvements.
|
||||
- Documentation including reference material and examples.
|
||||
- Bug and feature reports.
|
||||
|
||||
---
|
||||
|
||||
## Steps to Contribute
|
||||
|
||||
Fixes and improvements can be directly addressed by sending a Pull Request on GitHub. Pull requests will be reviewed by one or more maintainers and merged when acceptable.
|
||||
|
||||
We ask that before contributing, please make the effort to coordinate with the maintainers of the project before submitting large or high impact PRs. This will prevent you from doing extra work that may or may not be merged.
|
||||
|
||||
Use your judgement about what constitutes a large change. If you aren't sure, send a message to the **#litmus-dev** slack or submit an issue on GitHub.
|
||||
|
||||
<br />
|
||||
|
||||
### **Sign your work with Developer Certificate of Origin**
|
||||
|
||||
To contribute to this project, you must agree to the Developer Certificate of Origin (DCO) for each commit you make. The DCO is a simple statement that you, as a contributor, have the legal right to make the contribution.
|
||||
|
||||
See the [DCO](https://developercertificate.org/) file for the full text of what you must agree to.
|
||||
|
||||
To successfully sign off your contribution you just add a line to every git commit message:
|
||||
|
||||
```git
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
```
|
||||
|
||||
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
|
||||
If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`. You can also use git [aliases](https://git-scm.com/book/tr/v2/Git-Basics-Git-Aliases) like `git config --global alias.ci 'commit -s'`. Now you can commit with git ci and the commit will be signed.
|
||||
|
||||
<br />
|
||||
|
||||
### **Submitting a Pull Request**
|
||||
|
||||
To submit any kinds of improvements, please consider the following:
|
||||
|
||||
- Submit an [issue](https://github.com/litmuschaos/litmus/issues) describing your proposed change. If you are just looking to pick an open issue do so from a list of [good-first-issues](https://github.com/litmuschaos/litmus/labels/good%20first%20issue) maintained [here](https://github.com/litmuschaos/litmus/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
||||
- We would promptly respond back to your issue
|
||||
- Fork this repository, develop and test your code changes. See the Highlighted Repositories section below to choose which area you would like to contribute to.
|
||||
- Create a `feature branch` from your forked repository and submit a pull request against this repo’s main branch.
|
||||
- If you are making a change to the user interface (UI), include a screenshot of the UI changes.
|
||||
- Follow the relevant coding style guidelines
|
||||
- For backend contributions, popular ones are the [Go Code Review Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) and the _Formatting_ and _style_ section of Peter Bourgon's [Go: Best Practices for Production Environments](https://peter.bourgon.org/go-in-production/#formatting-and-style).
|
||||
- If you are making any changes in backend, make sure you have run and tested the code locally, the reviewers might ask for relevant screenshots in the comments.
|
||||
- For frontend contributions, we follow the [Airbnb style guide](https://airbnb.io/javascript/react/)
|
||||
- Your branch may be merged once all configured checks pass, including:
|
||||
- The branch has passed tests in CI.
|
||||
- A review from appropriate maintainers (see [MAINTAINERS.md](https://github.com/litmuschaos/litmus/blob/master/MAINTAINERS) and [GOVERNANCE.md](https://github.com/litmuschaos/litmus/blob/master/GOVERNANCE.md))
|
||||
|
||||
If you are new to Go, consider reading [Effective Go](https://golang.org/doc/effective_go.html) and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) for guidance on writing idiomatic Go code.
|
||||
|
||||
### Generating/Updating Mocks for `chaoscenter/graphql/server`
|
||||
|
||||
To generate new mocks or update existing mocks:
|
||||
|
||||
- Follow the instructions to install [mockery](https://vektra.github.io/mockery/latest/installation/).
|
||||
- If generating mocks for existing interface simply run `mockery`.
|
||||
- If generating mocks for new interface update [`.mockery.yaml`](././chaoscenter/graphql/server/.mockery.yaml) and run `mockery`.
|
||||
|
||||
---
|
||||
|
||||
## Pull Request Checklist :
|
||||
* Rebase to the current master branch before submitting your pull request.
|
||||
* Commits should be as small as possible. Each commit should follow the checklist below:
|
||||
|
||||
- Rebase to the current master branch before submitting your pull request.
|
||||
- Commits should be as small as possible. Each commit should follow the checklist below:
|
||||
- For code changes, add tests relevant to the fixed bug or new feature
|
||||
- Pass the compile and tests - includes spell checks, formatting, etc
|
||||
- Pass the compile and tests in CI
|
||||
- Commit header (first line) should convey what changed
|
||||
- Commit body should include details such as why the changes are required and how the proposed changes
|
||||
- DCO Signed
|
||||
|
||||
* If your PR is not getting reviewed or you need a specific person to review it, please reach out to the Litmus contributors at the [Litmus slack channel](https://app.slack.com/client/T09NY5SBT/CNXNB0ZTN)
|
||||
- DCO Signed
|
||||
- If your PR is not getting reviewed or you need a specific person to review it, please reach out to the Litmus contributors at the [Litmus slack channel](https://app.slack.com/client/T09NY5SBT/CNXNB0ZTN)
|
||||
|
||||
## Sign your work
|
||||
## Highlighted Repositories
|
||||
|
||||
We use the Developer Certificate of Origin (DCO) as an additional safeguard for the LitmusChaos project. This is a well established and widely used mechanism to assure that contributors have confirmed their right to license their contribution under the project's license. Please add a line to every git commit message:
|
||||
You can choose from a list of sub-dependent repos to contribute to, a few highlighted repos that Litmus uses are:
|
||||
|
||||
```
|
||||
Signed-off-by: Random J Developer <random@developer.example.org>
|
||||
```
|
||||
|
||||
Use your real name (sorry, no pseudonyms or anonymous contributions). The email id should match the email id provided in your GitHub profile.
|
||||
If you set your `user.name` and `user.email` in git config, you can sign your commit automatically with `git commit -s`.
|
||||
|
||||
You can also use git [aliases](https://git-scm.com/book/tr/v2/Git-Basics-Git-Aliases) like `git config --global alias.ci 'commit -s'`. Now you can commit with `git ci` and the commit will be signed.
|
||||
- [Chaos-charts](https://github.com/litmuschaos/chaos-charts)
|
||||
- [Chaos-workflows](https://github.com/litmuschaos/chaos-workflows)
|
||||
- [Test-tools](https://github.com/litmuschaos/test-tools)
|
||||
- [Litmus-go](https://github.com/litmuschaos/litmus-go)
|
||||
- [Litmus-website](https://github.com/litmuschaos/litmus-website-2)
|
||||
- [Litmusctl](https://github.com/litmuschaos/litmusctl)
|
||||
- [Litmus-docs](https://github.com/litmuschaos/litmus-docs)
|
||||
- [backstage-plugin](https://github.com/litmuschaos/backstage-plugin)
|
||||
|
||||
## Community
|
||||
|
||||
The litmus community will have a weekly contributor sync-up on Tuesdays 16.00-16.30IST / 12.30-13.00CEST
|
||||
The litmus community will have a weekly contributor sync-up on Tuesdays 16.00-16.30 IST / 12.30-13.00 CEST
|
||||
|
||||
- The sync up meeting is held online on [Google Hangouts](https://meet.google.com/uvt-ozaw-bvp)
|
||||
- The release items are tracked in this [planning sheet](https://docs.google.com/spreadsheets/d/15svGB99bDcSTkwAYttH1QzP5WJSb-dFKbPzl-9WqmXM).
|
||||
- The release items are tracked in the [release sheet](https://github.com/litmuschaos/litmus/releases).
|
||||
|
|
150
GOVERNANCE.md
150
GOVERNANCE.md
|
@ -1,105 +1,38 @@
|
|||
# LitmusChaos Project Governance
|
||||
|
||||
This document outlines the governance structure for the LitmusChaos project, a CNCF Incubating project. It describes the roles, responsibilities, decision-making processes, and mechanisms for community involvement.
|
||||
|
||||
We abide by the [Code of Conduct](./CODE_OF_CONDUCT.md) for all the projects maintained under the LitmusChaos Organization.
|
||||
|
||||
For specific guidance on practical contribution steps for any LitmusChaos sub-project please
|
||||
see our [CONTRIBUTING.md](./CONTRIBUTING.md) guide and the sub-project specific contributing guides
|
||||
see our [CONTRIBUTING.md](./CONTRIBUTING.md) guide and the sub-project specific contributing guides
|
||||
in the respective GitHub repositories.
|
||||
|
||||
## Maintainership
|
||||
## Roles and Membership
|
||||
|
||||
There are different types of maintainers, with different responsibilities, but
|
||||
all maintainers have 3 things in common:
|
||||
Roles and their responsibilities are detailed in the [Community Membership](./community-roles.md) document.
|
||||
|
||||
1) They share responsibility in the project's success.
|
||||
2) They have made a long-term, recurring time investment to improve the project.
|
||||
3) They spend that time doing whatever needs to be done, not necessarily what
|
||||
is the most interesting or fun.
|
||||
The list of current maintainers and their organizational affiliations is maintained in the [MAINTAINERS.md](./MAINTAINERS.md) file.
|
||||
|
||||
Maintainers are often under-appreciated, because their work is harder to appreciate.
|
||||
It's easy to appreciate a really cool and technically advanced feature. It's harder
|
||||
to appreciate the absence of bugs, the slow but steady improvement in stability,
|
||||
or the reliability of a release process. But those things distinguish a great
|
||||
project from a good one.
|
||||
## Conflict Resolution and Voting
|
||||
|
||||
## Reviewers
|
||||
Most issues within the project are resolved by consensus. When consensus cannot be reached, a voting process is initiated. All decisions are documented publicly, either in GitHub or in meeting notes.
|
||||
|
||||
A reviewer is a core role within the project.
|
||||
They share in reviewing issues and pull requests and their LGTM counts towards the
|
||||
required LGTM count to merge a code change into the project.
|
||||
### Voting Process
|
||||
|
||||
Reviewers are part of the organization but do not have write access.
|
||||
Becoming a reviewer is a core aspect in the journey to becoming a maintainer.
|
||||
|
||||
## Adding maintainers
|
||||
|
||||
Maintainers are first and foremost contributors that have shown they are
|
||||
committed to the long term success of a project. Contributors wanting to become
|
||||
maintainers are expected to be deeply involved in contributing code, pull
|
||||
request review, and triage of issues in the project for more than three months.
|
||||
|
||||
Just contributing does not make you a maintainer, it is about building trust
|
||||
with the current maintainers of the project and being a person that they can
|
||||
depend on and trust to make decisions in the best interest of the project.
|
||||
|
||||
Periodically, the existing maintainers curate a list of contributors that have
|
||||
shown regular activity on the project over the prior months. From this list,
|
||||
maintainer candidates are selected and proposed maintainers slack channel.
|
||||
|
||||
After a candidate has been announced on the maintainers slack channel, the
|
||||
existing maintainers are given five business days to discuss the candidate,
|
||||
raise objections and cast their vote. The Votes take place via the pull request
|
||||
comment. Candidates must be approved by at least 66% of the
|
||||
current maintainers by adding their vote on the mailing list. The reviewer role
|
||||
has the same process but only requires 33% of current maintainers. Only
|
||||
maintainers of the repository that the candidate is proposed for are allowed to
|
||||
vote.
|
||||
|
||||
If a candidate is approved, a maintainer will contact the candidate to invite
|
||||
the candidate to open a pull request that adds the contributor to the
|
||||
MAINTAINERS file. The voting process may take place inside a pull request if a
|
||||
maintainer has already discussed the candidacy with the candidate and a
|
||||
maintainer is willing to be a sponsor by opening the pull request. The candidate
|
||||
becomes a maintainer once the pull request is merged.
|
||||
|
||||
## Adding sub-projects
|
||||
|
||||
Similar to adding maintainers, new sub projects can be added to LitmusChaos
|
||||
GitHub organization as long as they adhere to the LitmusChaos vision and mission.
|
||||
New projects are discussed in either the Contributor Meeting or the Community
|
||||
slack and requires at least 1 maintainer approval.
|
||||
|
||||
If a project is approved, a maintainer will add the project to the LitmusChaos
|
||||
GitHub organization, and make an announcement on a public forum.
|
||||
|
||||
## Stepping down policy
|
||||
|
||||
Life priorities, interests, and passions can change. If you're a maintainer but
|
||||
feel you must remove yourself from the list, inform other maintainers that you
|
||||
intend to step down, and if possible, help find someone to pick up your work.
|
||||
At the very least, ensure your work can be continued where you left off.
|
||||
|
||||
After you've informed other maintainers, create a pull request to remove
|
||||
yourself from the MAINTAINERS file.
|
||||
|
||||
## Removal of inactive maintainers
|
||||
|
||||
Similar to the procedure for adding new maintainers, existing maintainers can
|
||||
be removed from the list if they do not show significant activity on the
|
||||
project. Periodically, the maintainers review the list of maintainers and their
|
||||
activity over the last three months.
|
||||
|
||||
If a maintainer has shown insufficient activity over this period, a neutral
|
||||
person will contact the maintainer to ask if they want to continue being
|
||||
a maintainer. If the maintainer decides to step down as a maintainer, they
|
||||
open a pull request to be removed from the MAINTAINERS file.
|
||||
- **Threshold:** A vote passes with a simple majority.
|
||||
- **Quorum:** At least 30% of maintainers must participate in the vote.
|
||||
- **Voting Method:** Votes are cast by adding +1 or -1 to the associated GitHub issue or PR.
|
||||
- **Binding Votes:** Each maintainer has one binding vote. Non-binding votes from the community are encouraged.
|
||||
- **Organizational Limit:** No single organization can cast more than 40% of the eligible votes. Organizations with more than 40% of maintainers must designate voting members.
|
||||
- **Duration:** Voting remains open for one week.
|
||||
|
||||
## How are decisions made?
|
||||
|
||||
LitmusChaos is an open-source project with an open design philosophy. This means
|
||||
that the repository is the source of truth for EVERY aspect of the project,
|
||||
including its philosophy, design, road map, and APIs. *If it's part of the
|
||||
project, it's in the repo. If it's in the repo, it's part of the project.*
|
||||
including its philosophy, design, road map, and APIs. _If it's part of the
|
||||
project, it's in the repo. If it's in the repo, it's part of the project._
|
||||
|
||||
As a result, all decisions can be expressed as changes to the repository. An
|
||||
implementation change is a change to the source code. An API change is a change
|
||||
|
@ -108,12 +41,39 @@ manifesto, and so on.
|
|||
|
||||
All decisions affecting LitmusChaos, big and small, follow the same 3 steps:
|
||||
|
||||
* Step 1: Open a pull request. Anyone can do this.
|
||||
- Step 1: Open a pull request. Anyone can do this.
|
||||
- Step 2: Discuss the pull request. Anyone can do this.
|
||||
- Step 3: Merge or refuse the pull request. Who does this depends on the nature
|
||||
of the pull request and which areas of the project it affects.
|
||||
|
||||
* Step 2: Discuss the pull request. Anyone can do this.
|
||||
## Decision-Making Process
|
||||
|
||||
* Step 3: Merge or refuse the pull request. Who does this depends on the nature
|
||||
of the pull request and which areas of the project it affects.
|
||||
Most decisions are made through consensus. If consensus cannot be reached, maintainers may initiate a vote.
|
||||
|
||||
### Voting
|
||||
|
||||
- **Threshold:** A vote passes with a simple majority.
|
||||
- **Quorum:** At least 30% of maintainers must participate in the vote.
|
||||
- **Method:** Votes are cast using +1 (approve) or -1 (reject) in the relevant GitHub PR or issue.
|
||||
- **Duration:** Voting remains open for one week.
|
||||
|
||||
## Community Support and Transparency
|
||||
|
||||
LitmusChaos aims for full transparency and inclusion in all governance activities. All decisions are made publicly and documented in the GitHub repositories or public meetings.
|
||||
|
||||
### Recurring Public Meetings
|
||||
|
||||
- #### Maintainers and Contributors Meeting
|
||||
|
||||
Covers technical issues, future milestones, and roadmaps. Also focused on governance, membership, and the future direction of the project.
|
||||
|
||||
- #### Community Meeting
|
||||
|
||||
Engages end users and the community with project updates, user presentations, and open discussions.
|
||||
|
||||
- #### Meeting Calendar
|
||||
|
||||
Please fill [this invite form](https://forms.gle/AsuXB2hbTG2TyD2d9) to be added to the calendar
|
||||
|
||||
## Helping contributors with the DCO
|
||||
|
||||
|
@ -137,10 +97,12 @@ When you add someone's DCO, please also add your own to keep a log.
|
|||
Yes. Nobody should ever push to master directly. All changes should be
|
||||
made through a pull request.
|
||||
|
||||
## Conflict Resolution
|
||||
## Adding sub-projects
|
||||
|
||||
If you have a technical dispute that you feel has reached an impasse with a
|
||||
subset of the community, any contributor may open an issue, specifically
|
||||
calling for a resolution vote of the current maintainers to resolve the dispute.
|
||||
The same voting quorums required (2/3) for adding and removing maintainers
|
||||
will apply to conflict resolution.
|
||||
Similar to adding maintainers, new sub projects can be added to LitmusChaos
|
||||
GitHub organization as long as they adhere to the LitmusChaos vision and mission.
|
||||
New projects are discussed in either the Contributor Meeting or the Community
|
||||
slack and requires at least 1 maintainer approval.
|
||||
|
||||
If a project is approved, a maintainer will add the project to the LitmusChaos
|
||||
GitHub organization, and make an announcement on a public forum.
|
||||
|
|
27
MAINTAINERS
27
MAINTAINERS
|
@ -1,27 +0,0 @@
|
|||
# Official list of LitmusChaos Maintainers.
|
||||
#
|
||||
# Names added to this file should be in the following format:
|
||||
# Individual's Name,@githubhandle, Email Address, Company Name
|
||||
#
|
||||
# Each of the LitmusChaos sub project may have one or more of the
|
||||
# following maintainers and list of reviewers who are
|
||||
# in the process of becoming maintainers.
|
||||
#
|
||||
# Please keep the below list sorted in ascending order.
|
||||
#
|
||||
#Maintainers
|
||||
|
||||
"Chandan Kumar",@chandankumar4,chandan.kumar@zopsmart.com,ZopSmart
|
||||
"Jayesh Kumar",@k8s-dev,tankjaye@amazon.com,Amazon
|
||||
"Karthik Satchitanand",@ksatchit,karthik.s@mayadata.io,MayaData
|
||||
"Maria Kotlyarevskaya",@Jasstkn,jasssstkn@yahoo.com,Wrike
|
||||
"Sumit Nagal",@sumitnagal,sumit_nagal@intuit.com,Intuit
|
||||
"Uma Mukkara",@umamukkara,uma@mayadata.io,MayaData
|
||||
|
||||
|
||||
#Reviewers
|
||||
"Amit Bhatt",@amitbhatt818,amit.bhatt@mayadata.io,MayaData
|
||||
"Rahul M Chheda",@rahulchheda,rahul.chheda1997@gmail.com,InfraCloud Technologies
|
||||
"Raj Das",@rajdas98,raj.das@mayadata.io,MayaData
|
||||
"Shubham Chaudhary",@ispeakc0de,shubham.chaudhary@mayadata.io,MayaData
|
||||
"Udit Gaurav",@uditgaurav,udit.gaurav@mayadata.io,MayaData
|
|
@ -0,0 +1,69 @@
|
|||
# LitmusChaos Maintainers
|
||||
|
||||
- [GOVERNANCE.md](./GOVERNANCE.md) describes the LitmusChaos governance.
|
||||
- [community-roles.md](./community-roles.md) describes the responsibilities and requirements on the project roles.
|
||||
|
||||
### Component-Wise Code Owners & Primary Reviewers
|
||||
|
||||
Area |Components |Source |Maintainers |Reviewers|
|
||||
-----------------|---------------------------------|-------------------------------------------------|--------------------------------------------|-------- |
|
||||
control-plane |chaos-manager |graphql-server |@amityt, @Jonsy13, @imrajdas, @SarthakJain26 |@gdsoumya, @Saranya-jena, @arkajyotiMukherjee|
|
||||
control-plane |chaos-dashboard |frontend, component-library |@arkajyotiMukherjee, @S-ayanide |@amityt, @SahilKr24, @hrishavjha|
|
||||
execution-plane |subscriber, event-tracker |cluster-agents |@gdsoumya, @imrajdas, @SarthakJain26 |@amityt, @Jonsy13, @ispeakc0de, @Adarshkumar14 |
|
||||
execution-plane |litmus-core |chaos-operator, chaos-runner, elves, chaos-exporter |@ksatchit, @ispeakc0de, @chandankumar4 |@uditgaurav, @neelanjan |
|
||||
chaos-experiments|experiment-lib, chaoshub |litmus-go, test-tools, chaos-charts |@uditgaurav, @ispeakc0de, @ksatchit, @Vr00mm| @neelanjan00, @Adarshkumar14, @avaakash |
|
||||
chaos-plugins |cli, plugin infra, developer portals |litmusctl, backstage-plugin |@Saranya-jena, @SarthakJain26, @namkyu1999 |@Jonsy13, @ajeshbaby, @imrajdas |
|
||||
chaos-sdk |go/python/ansible sdk |litmus-go,litmus-python,litmus-ansible |@oumkale, @ispeakc0de, @ksatchit |@neelanjan00, @avaakash, @uditgaurav |
|
||||
e2e |e2e-suite, e2e-dashboard |litmus-e2e |@uditgaurav, @Jonsy13 |@neelanjan00, @S-ayanide, @avaakash |
|
||||
integrations |CI/CD plugins, wrappers |chaos-ci-lib, gitlab-templates, github-actions |@uditgaurav, @ksatchit |@ispeakc0de, @Adarshkumar14 |
|
||||
helm-charts |control-plane, agent, experiments|litmus-helm |@Jasstkn, @ispeakc0de, @imrajdas, @Jonsy13 |@ksatchit, @uditgaurav |
|
||||
documentation |platform-docs, experiment-docs |litmus-docs, mkdocs |@neelanjan00, @umamukkara, @ispeakc0de |@ksatchit, @ajeshbaby, @amityt, @uditgaurav |websites |project website, chaoshub, documentation |litmus-website, charthub, litmus-docs |@umamukkara, @arkajyotiMukherjee, @S-ayanide |@SahilKr24, @hrishavjha, @ajeshbaby |
|
||||
websites |project website, chaoshub, documentation |litmus-website, charthub, litmus-docs |@SahilKr24, @hrishavjha, @ajeshbaby |@umamukkara, @S-ayanide |
|
||||
|
||||
### Consolidated Maintainers List
|
||||
|
||||
```
|
||||
"Amit Kumar Das",@amityt,amit.das@harness.io
|
||||
"Arkojyoti Mukherjee",@arkajyotiMukherjee,arko@harness.io
|
||||
"Chandan Kumar",@chandankumar4,ckamtaprasad@msystechnologies.com
|
||||
"Karthik Satchitanand",@ksatchit,karthik.s@harness.io
|
||||
"Maria Kotlyarevskaya",@Jasstkn,jasssstkn@yahoo.com
|
||||
"Namkyu Park",namkyu1999,lak9348@gmail.com
|
||||
"Neelanjan Manna",@neelanjan00,neelanjan.manna@harness.io
|
||||
"Oum Nivrati Kale",@oumkale,oumk@jfrog.com
|
||||
"Raj Das",@imrajdas,rajbabu.das@harness.io
|
||||
"Rémi Ziolkowski",@Vr00mm,remi.ziolkowski-ext@pole-emploi.fr
|
||||
"Soumya Ghosh Dastidar",@gdsoumya,gdsoumya@gmail.com
|
||||
"Saranya Jena",@Saranya-jena,saranya.jena@harness.io
|
||||
"Sarthak Jain",@SarthakJain26,sarthak.jain@harness.io
|
||||
"Sayan Mondal",@S-ayanide,sayan.mondal@harness.io
|
||||
"Shubham Chaudhary",@ispeakc0de,shubham.chaudhary@harness.io
|
||||
"Udit Gaurav",@uditgaurav,udit.gaurav@harness.io
|
||||
"Vedant Shrotria",@Jonsy13,vedant.shrotria@harness.io
|
||||
"Uma Mukkara",@umamukkara,umasankar.mukkara@harness.io
|
||||
"Sahil KR",@SahilKr24,sahil.kumar@harness.io
|
||||
"Ajesh Baby",@ajeshbaby,ajesh.baby@harness.io
|
||||
"Hrishav Kumar",@hrishavjha,hrishav.kumar@harness.io
|
||||
```
|
||||
|
||||
### Consolidated Reviewers List
|
||||
|
||||
```
|
||||
"Adarsh Kumar",@Adarshkumar14,adarsh.kumar@harness.io
|
||||
"Akash Srivastava",@avaakash,akash.srivastava@harness.io
|
||||
```
|
||||
|
||||
### Emeritus Maintainers
|
||||
|
||||
```
|
||||
"Jayesh Kumar",@k8s-dev,tankjaye@amazon.com,Amazon
|
||||
"Sumit Nagal",@sumitnagal,snagal@salesforce.com,Salesforce
|
||||
```
|
||||
|
||||
### Emeritus Reviewers
|
||||
|
||||
```
|
||||
"Amit Bhatt",@amitbhatt818,amit.bhatt@mayadata.io,MayaData
|
||||
"Ishan Gupta",@ishangupta-ds,ishan@chaosnative.com,ChaosNative
|
||||
"Rahul M Chheda",@rahulchheda,rahul.chheda1997@gmail.com,Independent
|
||||
```
|
|
@ -0,0 +1,26 @@
|
|||
# LITMUSCHAOS MENTORING
|
||||
|
||||
This document serves as a comprehensive record of mentees, mentors, issues, and blogs associated with prominent open source programs such as LFX Mentorship, Google Summer of Code, Google Season of Docs, and Outreachy. Its primary objective is to provide an organized overview of mentoring activities and effectively track the progress made within the project.
|
||||
|
||||
## Mentoring Details
|
||||
|
||||
| Program | Timeline | Mentee | Mentor(s) | Github Issue | Blog(s) |
|
||||
|-----------------------------|-------------------------------------|-------------------------------------------------|----------------------------------------------------------------------------------------------------|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Google Summer of Code (GSoC) | May 17th - August 31st, 2021 | [Hemanth Krishna](https://github.com/DarthBenro008) | [Raj Babu Das](https://github.com/imrajdas), [Karthik S](https://github.com/ksatchit) | https://github.com/litmuschaos/litmus/issues/2483 | [Blog](https://darthbenro008.medium.com/google-summer-of-code-2021-with-cloud-native-compute-foundation-my-experience-with-litmuschaos-18f1ec3e5bfa), [CNCF blog](https://www.cncf.io/blog/2021/08/19/__trashed-2/) |
|
||||
| LFX Mentorship | September 1st - November 31st, 2021 | [Dhananjay Purohit](https://github.com/DhananjayPurohit) | [Vedant Shrotria](https://github.com/jonsy13), [Raj Babu Das](https://github.com/imrajdas) | https://github.com/litmuschaos/litmus/issues/3114 | [Blog](https://dhananjaypurohit.medium.com/getting-started-my-journey-with-lfx-mentorship-and-litmuschaos-3514eacf6df2) |
|
||||
| LFX Mentorship | September 1st - November 31st, 2021 | [Aman Dwivedi](https://github.com/Aman-Codes) | [Udit Gaurav](https://github.com/uditgaurav), [Soumya Ghosh Dastidar](https://github.com/gdsoumya) | https://github.com/litmuschaos/litmus/issues/3112 | [Blog](https://aman-codes.medium.com/kick-start-of-my-lfx-mentorship-with-litmus-chaos-eeb089a10951), [CNCF blog](https://www.cncf.io/blog/2022/08/11/my-experience-contributing-to-litmuschaos-as-a-student) |
|
||||
| LFX Mentorship | March 1st - May 31st, 2022 | [Prayag Savsani](https://github.com/PrayagS) | [Saranya Jena](https://github.com/Saranya-jena), [Sarthak Jain](https://github.com/SarthakJain26) | https://github.com/litmuschaos/litmus/issues/3440 | -- |
|
||||
| LFX Mentorship | March 1st - May 31st, 2023 | [NamKyu Park](https://github.com/namkyu1999) | [Sayan Mondal](https://github.com/S-ayanide), [Amit Kumar Das](https://github.com/amityt) | https://github.com/litmuschaos/litmus/issues/3892 | [Blog](https://dev.to/namkyu1999/my-lfx-mentorship-journey-the-best-starting-point-for-contributing-to-open-source-4f35), [CNCF blog](https://www.cncf.io/blog/2023/05/30/my-lfx-mentorship-journey-the-best-starting-point-for-contributing-to-open-source/) |
|
||||
| LFX Mentorship | June 1st - August 31st, 2023 | [Soham Ratnaparkhi](https://github.com/SohamRatnaparkhi) | [Amit Kumar Das](https://github.com/amityt), [Arkajyoti Mukherjee](https://github.com/arkajyotiMukherjee) | https://github.com/litmuschaos/litmus/issues/3970 | -- |
|
||||
| LFX Mentorship | June 1st - August 31st, 2023 | [Nagesh Bansal](https://github.com/Nageshbansal) | [Shubham Chaudhary](https://github.com/ispeakc0de), [Vansh Bhatia](https://github.com/vanshBhatia-A4k9) | https://github.com/litmuschaos/litmus/issues/3969 | [Blog](https://dev.to/nageshbansal/exploring-litmusctl-a-comprehensive-guide-170k) |
|
||||
| LFX Mentorship | September 1st - November 30th, 2023 | [Magnim Thibaut Freedisch Batale](https://github.com/Freedisch) | [Saranya Jena](https://github.com/Saranya-jena), [Sayan Mondal](https://github.com/S-ayanide) | https://github.com/litmuschaos/litmus/issues/4102 | [Blog](https://dev.to/freedisch_10/embarking-on-a-professional-growth-adventure-insights-from-my-lfx-mentorship-program-at-litmuschaos-5cbc) |
|
||||
| LFX Mentorship | September 1st - November 30th, 2023 | [Deep Poharkar](https://github.com/deep-poharkar) | [Sarthak Jain](https://github.com/SarthakJain26), [Neelanjan Manna](https://github.com/neelanjan00) | https://github.com/litmuschaos/litmus/issues/4101 | -- |
|
||||
| LFX Mentorship | March 1st - April 31st, 2024 | [Aryan Bhokare](https://github.com/aryan-bhokare) | [Saranya Jena](https://github.com/Saranya-jena), [Hrishav Kumar](https://github.com/hrishavjha), [Sahil Kumar](https://github.com/SahilKr24) | https://github.com/litmuschaos/litmus/issues/4407 | --
|
||||
| LFX Mentorship | March 1st - April 31st, 2024 | [Shivam Purohit](https://github.com/shivam-Purohit) | [Sarthak Jain](https://github.com/SarthakJain26), [Vedant Shrotria](https://github.com/jonsy13), [Nagesh Bansal](https://github.com/Nageshbansal) | https://github.com/litmuschaos/litmus/issues/4405 | --
|
||||
| LFX Mentorship | March 1st - April 31st, 2024 | [M R DHANUSH](https://github.com/Dhanush0369) | [Raj Babu Das](https://github.com/imrajdas), [Shubham Chaudhary](https://github.com/ispeakc0de), [NamKyu Park](https://github.com/namkyu1999) | https://github.com/litmuschaos/litmus/issues/4406 | -- |
|
||||
|
||||
> Refer to the [CNCF Mentoring](https://github.com/cncf/mentoring) repository for more details.
|
||||
|
||||
We sincerely thank all the mentors, mentees, organizations, and programs involved in this project for their invaluable support and contributions. Their dedication and commitment play a vital role in the success of our mentoring initiatives.
|
||||
|
||||
--x--
|
166
README.md
166
README.md
|
@ -1,18 +1,18 @@
|
|||
<img alt="LitmusChaos" src="https://landscape.cncf.io/logos/litmus.svg" width="200" align="left">
|
||||
<img alt="LitmusChaos" src="https://avatars.githubusercontent.com/u/49853472?s=200&v=4" width="200" align="left">
|
||||
|
||||
# Litmus
|
||||
### Cloud-Native Chaos Engineering
|
||||
# [LitmusChaos](https://litmuschaos.io/)
|
||||
### Open Source Chaos Engineering Platform
|
||||
|
||||
[](https://slack.litmuschaos.io)
|
||||
[](https://app.circleci.com/pipelines/github/litmuschaos/litmus)
|
||||

|
||||
[](https://hub.docker.com/r/litmuschaos/chaos-operator)
|
||||
[](https://github.com/litmuschaos/litmus/stargazers)
|
||||
[](https://github.com/litmuschaos/litmus/issues)
|
||||
[](https://twitter.com/LitmusChaos)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/3202)
|
||||
[](https://bettercodehub.com/)
|
||||
[](https://www.bestpractices.dev/projects/3202)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Flitmuschaos%2Flitmus?ref=badge_shield)
|
||||
[](https://www.youtube.com/channel/UCa57PMqmz_j0wnteRa9nCaw)
|
||||
[](https://gurubase.io/g/litmuschaos)
|
||||
<br><br><br><br>
|
||||
|
||||
#### *Read this in [other languages](translations/TRANSLATIONS.md).*
|
||||
|
@ -22,53 +22,139 @@
|
|||
|
||||
## Overview
|
||||
|
||||
Litmus is a toolset to do cloud-native chaos engineering. Litmus provides tools to orchestrate chaos on Kubernetes to help SREs find weaknesses in their deployments. SREs use Litmus to run chaos experiments initially in the staging environment and eventually in production to find bugs, vulnerabilities. Fixing the weaknesses leads to increased resilience of the system.
|
||||
LitmusChaos is an open source Chaos Engineering platform that enables teams to identify weaknesses & potential outages in infrastructures by
|
||||
inducing chaos tests in a controlled way. Developers & SREs can practice Chaos Engineering with LitmusChaos as it is easy to use, based on modern
|
||||
Chaos Engineering principles & community collaborated. It is 100% open source & a CNCF project.
|
||||
|
||||
Litmus takes a cloud-native approach to create, manage and monitor chaos. Chaos is orchestrated using the following Kubernetes Custom Resource Definitions (**CRDs**):
|
||||
LitmusChaos takes a cloud-native approach to create, manage and monitor chaos. The platform itself runs as a set of microservices and uses Kubernetes
|
||||
custom resources (CRs) to define the chaos intent, as well as the steady state hypothesis.
|
||||
|
||||
- **ChaosEngine**: A resource to link a Kubernetes application or Kubernetes node to a ChaosExperiment. ChaosEngine is watched by Litmus' Chaos-Operator which then invokes Chaos-Experiments
|
||||
- **ChaosExperiment**: A resource to group the configuration parameters of a chaos experiment. ChaosExperiment CRs are created by the operator when experiments are invoked by ChaosEngine.
|
||||
- **ChaosResult**: A resource to hold the results of a chaos-experiment. The Chaos-exporter reads the results and exports the metrics into a configured Prometheus server.
|
||||
At a high-level, Litmus comprises of:
|
||||
|
||||
Chaos experiments are hosted on <a href="https://hub.litmuschaos.io" target="_blank">hub.litmuschaos.io</a>. It is a central hub where the application developers or vendors share their chaos experiments so that their users can use them to increase the resilience of the applications in production.
|
||||
- **Chaos Control Plane**: A centralized chaos management tool called chaos-center, which helps construct, schedule and visualize Litmus chaos workflows
|
||||
- **Chaos Execution Plane Services**: Made up of a chaos agent and multiple operators that execute & monitor the experiment within a defined
|
||||
target Kubernetes environment.
|
||||
|
||||

|
||||

|
||||
|
||||
At the heart of the platform are the following chaos custom resources:
|
||||
|
||||
- **ChaosExperiment**: A resource to group the configuration parameters of a particular fault. ChaosExperiment CRs are essentially installable templates
|
||||
that describe the library carrying out the fault, indicate permissions needed to run it & the defaults it will operate with. Through the ChaosExperiment, Litmus supports BYOC (bring-your-own-chaos) that helps integrate (optional) any third-party tooling to perform the fault injection.
|
||||
|
||||
- **ChaosEngine**: A resource to link a Kubernetes application workload/service, node or an infra component to a fault described by the ChaosExperiment.
|
||||
It also provides options to tune the run properties and specify the steady state validation constraints using 'probes'. ChaosEngine is watched by the
|
||||
Chaos-Operator, which reconciles it (triggers experiment execution) via runners.
|
||||
|
||||
The ChaosExperiment & ChaosEngine CRs are embedded within a Workflow object that can string together one or more experiments in a desired order.
|
||||
|
||||
- **ChaosResult**: A resource to hold the results of the experiment run. It provides details of the success of each validation constraint,
|
||||
the revert/rollback status of the fault as well as a verdict. The Chaos-exporter reads the results and exposes information as prometheus metrics.
|
||||
ChaosResults are especially useful during automated runs.
|
||||
|
||||
ChaosExperiment CRs are hosted on <a href="https://hub.litmuschaos.io" target="_blank">hub.litmuschaos.io</a>. It is a central hub where the
|
||||
application developers or vendors share their chaos experiments so that their users can use them to increase the resilience of the applications
|
||||
in production.
|
||||
|
||||
## Use cases
|
||||
|
||||
- **For Developers**: To run chaos experiments during application development as an extension of unit testing or integration testing.
|
||||
- **For CI pipeline builders**: To run chaos as a pipeline stage to find bugs when the application is subjected to fail paths in a pipeline.
|
||||
- **For SREs**: To plan and schedule chaos experiments into the application and/or surrounding infrastructure. This practice identifies the weaknesses in the system and increases resilience.
|
||||
- **For CI/CD pipeline builders**: To run chaos as a pipeline stage to find bugs when the application is subjected to fail paths in a pipeline.
|
||||
- **For SREs**: To plan and schedule chaos experiments into the application and/or surrounding infrastructure. This practice identifies the weaknesses
|
||||
in the deployment system and increases resilience.
|
||||
|
||||
## Getting Started with Litmus
|
||||
|
||||
[](https://youtu.be/W5hmNbaYPfM)
|
||||
|
||||
Check out the <a href="https://docs.litmuschaos.io/docs/next/getstarted.html" target="_blank">Litmus Docs</a> to get started.
|
||||
To get started, check out the <a href="https://docs.litmuschaos.io/docs/introduction/what-is-litmus" target="_blank">Litmus Docs</a> and specifically the <a href="https://docs.litmuschaos.io/docs/getting-started/installation#prerequisites" target="_blank">Installation section</a> of the <a href="https://docs.litmuschaos.io/docs/getting-started/installation" target="_blank">Getting Started with Litmus</a> page.
|
||||
|
||||
## Contributing to Chaos Hub
|
||||
|
||||
Check out the <a href="https://github.com/litmuschaos/community-charts/blob/master/CONTRIBUTING.md" target="_blank">Contributing Guidelines for the Chaos Hub</a>
|
||||
|
||||
|
||||
## Community
|
||||
|
||||
### Community Resources:
|
||||
|
||||
Feel free to reach out if you have any queries,concerns, or feature requests
|
||||
|
||||
- Give us a star ⭐️ - If you are using LitmusChaos or think it is an interesting project, we would love a star ❤️
|
||||
|
||||
- Follow LitmusChaos on Twitter [@LitmusChaos](https://twitter.com/LitmusChaos).
|
||||
|
||||
- Subscribe to the [LitmusChaos YouTube channel](https://www.youtube.com/channel/UCa57PMqmz_j0wnteRa9nCaw) for regular updates & meeting recordings.
|
||||
|
||||
- To join our [Slack Community](https://slack.litmuschaos.io/) and meet our community members, put forward your questions & opinions, join the #litmus channel on the [Kubernetes Slack](https://slack.k8s.io/).
|
||||
### Community Meetings
|
||||
|
||||
1. Community Meetings
|
||||
These will be hosted every 3rd Wednesday of every month at 5:30 PM GMT /6:30 PM CEST /10 PM IST
|
||||
The community meetings will involve discussing community updates, sharing updates on new features/releases and discussing user/adopter stories. Everyone in the community is invited for the same to participate in the LitmusChaos community meetings.
|
||||
|
||||
|
||||
2. Contributor Meetings
|
||||
These will be hosted every second & last Thursday of every month at 2:30 PM GMT /3:30 PM CEST /7 PM IST
|
||||
The contributor meetings are only meant to discuss technical and non-technical contributions to LitmusChaos. Maintainers, present Contributors and aspiring contributors are invited to participate in the LitmusChaos contributor meetings to discuss issues, fixes, enhancements and future contributions
|
||||
|
||||
|
||||
Fill out the [LitmusChaos Meetings invite form](https://forms.gle/xYZyZ2gTWMqz7xSs7) to get your Calendar invite!
|
||||
|
||||
|
||||
- [Sync Up Meeting Link](https://harness-io.zoom.us/j/95100368978?pwd=b2VrdCtaakE5U3dhOElFMUJOaXVOUT09)
|
||||
- [Sync Up Agenda & Meeting Notes](https://hackmd.io/a4Zu_sH4TZGeih-xCimi3Q)
|
||||
- [Release Tracker](https://github.com/litmuschaos/litmus/milestones)
|
||||
|
||||
### Videos
|
||||
|
||||
- [What if Your System Experiences an Outage? Let's Build a Resilient Systems with Chaos Engineering](https://www.youtube.com/watch?v=3mjGEh905u4&t=1s) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Enhancing Cyber Resilience Through Zero Trust Chaos Experiments in Cloud Native Environments](https://youtu.be/BelNIk4Bkng) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [LitmusChaos, with Karthik Satchitanand](https://www.youtube.com/watch?v=ks2R57hhFZk&t=503s) @ [The Kubernetes Podcast from Google](https://www.youtube.com/@TheKubernetesPodcast)
|
||||
- [Cultural Shifts: Fostering a Chaos First Mindset in Platform Engineering](https://www.youtube.com/watch?v=WUXFKxgZRsk) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Fire in the Cloud: Bringing Managed Services Under the Ambit of Cloud-Native Chaos Engineering](https://www.youtube.com/watch?v=xCDQp5E3VUs) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Security Controls for Safe Chaos Experimentation](https://www.youtube.com/watch?v=whCkvLKAw74) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Chaos Engineering For Hybrid Targets With LitmusChaos](https://www.youtube.com/watch?v=BZL-ngvbpbU&t=751s) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Cloud Native Live: Litmus Chaos Engine and a microservices demo app](https://youtu.be/hOghvd9qCzI)
|
||||
- [Chaos Engineering hands-on - An SRE ideating Chaos Experiments and using LitmusChaos | July 2022](https://youtu.be/_x_7SiesjF0)
|
||||
- [Achieve Digital Product Resiliency with Chaos Engineering](https://youtu.be/PQrmBHgk0ps)
|
||||
- [Case Study: Bringing Chaos Engineering to the Cloud Native Developers](https://youtu.be/KSl-oKk6TPA) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [Cloud Native Chaos Engineering with LitmusChaos](https://www.youtube.com/watch?v=ItUUqejdXr0) @ [CNCF](https://www.youtube.com/@cncf)
|
||||
- [How to create Chaos Experiments with Litmus | Litmus Chaos tutorial](https://youtu.be/mwu5eLgUKq4) @ [Is it Observable](https://www.youtube.com/c/IsitObservable)
|
||||
- [Cloud Native Chaos Engineering Preview With LitmusChaos](https://youtu.be/pMWqhS-F3tQ)
|
||||
- [Get started with Chaos Engineering with Litmus](https://youtu.be/5CI8d-SKBfc) @ [Containers from the Couch](https://www.youtube.com/c/ContainersfromtheCouch)
|
||||
- [Litmus 2 - Chaos Engineering Meets Argo Workflows](https://youtu.be/B8DfYnDh2F4) @ [DevOps Toolkit](https://youtube.com/c/devopstoolkit)
|
||||
- [Hands-on with Litmus 2.0 | Rawkode Live](https://youtu.be/D0t3emVLLko) @ [Rawkode Academy](https://www.youtube.com/channel/UCrber_mFvp_FEF7D9u8PDEA)
|
||||
- [Introducing LitmusChaos 2.0 / Dok Talks #74](https://youtu.be/97BiCNtJbDw) @ [DoK.community](https://www.youtube.com/channel/UCUnXJbHQ89R2uSfKsqQwGvQ)
|
||||
- [Introduction to Cloud Native Chaos Engineering](https://youtu.be/LK0oDLQE4S8) @ [Kunal Kushwaha](https://www.youtube.com/channel/UCBGOUQHNNtNGcGzVq5rIXjw)
|
||||
- [#EveryoneCanContribute cafe: Litmus - Chaos Engineering for your Kubernetes](https://youtu.be/IiyrEiK4stQ) @ [GitLab Unfiltered](https://www.youtube.com/channel/UCMtZ0sc1HHNtGGWZFDRTh5A)
|
||||
- [Litmus - Chaos Engineering for Kubernetes (CNCFMinutes 9)](https://youtu.be/rDQ9XKbSJIc) @ [Saiyam Pathak](https://www.youtube.com/channel/UCi-1nnN0eC9nRleXdZA6ncg)
|
||||
- [Chaos Engineering with Litmus Chaos by Prithvi Raj || HACKODISHA Workshop](https://youtu.be/eyAG0svCsQA) @ [Webwiz](https://www.youtube.com/channel/UC9yM_PkV0QIIsPA3qPrp)
|
||||
|
||||
[And More....](https://www.youtube.com/channel/UCa57PMqmz_j0wnteRa9nCaw)
|
||||
|
||||
### Blogs
|
||||
|
||||
- CNCF: [Introduction to LitmusChaos](https://www.cncf.io/blog/2020/08/28/introduction-to-litmuschaos/)
|
||||
- Hackernoon: [Manage and Monitor Chaos via Litmus Custom Resources](https://hackernoon.com/solid-tips-on-how-to-manage-and-monitor-chaos-via-litmus-custom-resources-5g1s33m9)
|
||||
- [Observability Considerations in Chaos: The Metrics Story](https://dev.to/ksatchit/observability-considerations-in-chaos-the-metrics-story-6cb)
|
||||
|
||||
Community Blogs:
|
||||
|
||||
- LiveWyer: [LitmusChaos Showcase: Chaos Experiments in a Helm Chart Test Suite](https://livewyer.io/blog/2021/03/22/litmuschaos-showcase-chaos-experiments-in-a-helm-chart-test-suite/)
|
||||
- Jessica Cherry: [Test Kubernetes cluster failures and experiments in your terminal](https://opensource.com/article/21/6/kubernetes-litmus-chaos)
|
||||
- Yang Chuansheng(KubeSphere): [KubeSphere 部署 Litmus 至 Kubernetes 开启混沌实验](https://kubesphere.io/zh/blogs/litmus-kubesphere/)
|
||||
- Saiyam Pathak(Civo): [Chaos Experiments on Kubernetes using Litmus to ensure your cluster is production ready](https://www.civo.com/learn/chaos-engineering-kubernetes-litmus)
|
||||
- Andreas Krivas(Container Solutions):[Comparing Chaos Engineering Tools for Kubernetes Workloads](https://blog.container-solutions.com/comparing-chaos-engineering-tools)
|
||||
- Akram Riahi(WeScale):[Chaos Engineering : Litmus sous tous les angles](https://blog.wescale.fr/2021/03/11/chaos-engineering-litmus-sous-tous-les-angles/)
|
||||
- Prashanto Priyanshu(LensKart):[Lenskart’s approach to Chaos Engineering-Part 2](https://blog.lenskart.com/lenskarts-approach-to-chaos-engineering-part-2-6290e4f3a74e)
|
||||
- DevsDay.ru(Russian):[LitmusChaos at Kubecon EU '21](https://devsday.ru/blog/details/40746)
|
||||
|
||||
|
||||
## Adopters
|
||||
|
||||
Check out the <a href="https://github.com/litmuschaos/litmus/blob/master/ADOPTERS.md" target="_blank">Adopters of LitmusChaos</a>
|
||||
|
||||
(_Send a PR to the above page if you are using Litmus in your chaos engineering practice_)
|
||||
|
||||
## Things to Consider
|
||||
|
||||
Some of the considerations that need to be made with Litmus (as a chaos framework), are broadly listed here. Many of these are already being worked on
|
||||
as mentioned in the [ROADMAP](./ROADMAP.md). For details or limitations around specific experiments, refer to the respective [experiments docs](https://docs.litmuschaos.io/docs/pod-delete/).
|
||||
|
||||
- Litmus chaos operator and the chaos experiments run as kubernetes resources in the cluster. In case of airgapped environments, the chaos custom resources
|
||||
and images need to be hosted on premise.
|
||||
- When attempting to execute platform specific chaos experiments (like those on AWS, GCP cloud) the access details are passed via kubernetes secrets. Support
|
||||
for other modes of secret management with Litmus is yet to be tested/implemented.
|
||||
- Some chaos experiments make use of the docker api from within the experiment pods, and thereby require the docker socket to be mounted. User discretion is
|
||||
advised when allowing developers/devops admins/SREs access for running these experiments.
|
||||
- In (rare) cases where chaos experiments make use of privileged containers, the recommended security policies will be documented.
|
||||
|
||||
## License
|
||||
|
||||
Litmus is licensed under the Apache License, Version 2.0. See [LICENSE](./LICENSE) for the full license text. Some of the projects used by the Litmus project may be governed by a different license, please refer to its specific license.
|
||||
|
@ -77,19 +163,7 @@ Litmus is licensed under the Apache License, Version 2.0. See [LICENSE](./LICENS
|
|||
|
||||
Litmus Chaos is part of the CNCF Projects.
|
||||
|
||||
[](https://landscape.cncf.io/selected=litmus)
|
||||
|
||||
## Community
|
||||
|
||||
The Litmus community meets on the third wednesday of every month at 10:00PM IST/9.30 AM PST.
|
||||
|
||||
Community Resources:
|
||||
|
||||
- [Community Slack](https://slack.litmuschaos.io)
|
||||
- [Sync Up Meeting Link](https://zoom.us/j/91358162694)
|
||||
- [Sync Up Agenda & Meeting Notes](https://hackmd.io/a4Zu_sH4TZGeih-xCimi3Q)
|
||||
- [Youtube Channel (demos, meeting recordings, virtual meetups)](https://www.youtube.com/channel/UCa57PMqmz_j0wnteRa9nCaw)
|
||||
- [Release Tracker](https://github.com/litmuschaos/litmus/milestones)
|
||||
[](https://landscape.cncf.io/?selected=litmus)
|
||||
|
||||
## Important Links
|
||||
|
||||
|
@ -97,6 +171,6 @@ Community Resources:
|
|||
Litmus Docs <img src="https://avatars0.githubusercontent.com/u/49853472?s=200&v=4" alt="Litmus Docs" height="15">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://landscape.cncf.io/selected=litmus">
|
||||
CNCF Landscape <img src="https://landscape.cncf.io/images/left-logo.svg" alt="Litmus on CNCF Landscape" height="15">
|
||||
<a href="https://landscape.cncf.io/?selected=litmus">
|
||||
CNCF Landscape <img src="https://landscape.cncf.io/images/cncf-landscape-horizontal-color.svg" alt="Litmus on CNCF Landscape" height="15">
|
||||
</a>
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
## RELEASE GUIDELINES
|
||||
# Versioning and Release Guidelines
|
||||
|
||||
This document details the versioning, release plan and release guidelines for LitmusChaos. Stability
|
||||
is a top goal for this project, and we hope that this document and the processes
|
||||
it entails will help to achieve that. It covers the release timelines, tracking, process, versioning
|
||||
numbering, support horizons, and API stability.
|
||||
|
||||
If you rely on LitmusChaos, it would be good to spend time understanding the
|
||||
areas of the API that are and are not supported and how they impact your
|
||||
project in the future.
|
||||
|
||||
This document will be considered a living document. Scheduled releases, supported timelines, and API stability guarantees will be updated here as they
|
||||
change.
|
||||
|
||||
If there is something that you require or this document leaves out, please
|
||||
reach out by [filing an issue](https://github.com/litmuschaos/litmus/issues).
|
||||
|
||||
- There is a scheduled release on the 15th of every month on the following repositories:
|
||||
- [Litmus](https://github.com/litmuschaos/litmus)
|
||||
|
@ -7,8 +22,6 @@
|
|||
- [Chaos-Exporter](https://github.com/litmuschaos/chaos-exporter)
|
||||
- [Chaos-Charts](https://github.com/litmuschaos/chaos-charts)
|
||||
|
||||
- Repositories use release version according to the [Semantic Versioning](https://semver.org/)
|
||||
|
||||
- Docker images with release tags are pushed upon creation of a github release
|
||||
|
||||
- Following are the docker images:
|
||||
|
@ -19,8 +32,6 @@
|
|||
|
||||
- The chaos chart bundles are created by publishing the github releases for the [chaos-charts](https://github.com/litmuschaos/chaos-charts) repo. This is picked by the chaos [charthub](https://hub.litmuschaos.io) for user download.
|
||||
|
||||
- Tracking of releases is done on Github [project board](https://github.com/litmuschaos/litmus/projects)
|
||||
|
||||
- The release flow consists of the following steps:
|
||||
|
||||
- Sprint Planning based on backlogs & feature requests from the community
|
||||
|
@ -32,6 +43,157 @@
|
|||
- Doc sanity tests
|
||||
- Litmus release with change log
|
||||
|
||||
## Releases
|
||||
|
||||
Releases of LitmusChaos will be versioned using dotted triples, similar to
|
||||
[Semantic Version](http://semver.org/). For the purposes of this document, we
|
||||
will refer to the respective components of this triple as
|
||||
`<major>.<minor>.<patch>`. The version number may have additional information,
|
||||
such as alpha, beta and release candidate qualifications. Such releases will be
|
||||
considered "pre-releases".
|
||||
|
||||
### Major and Minor Releases
|
||||
|
||||
Major and minor releases of LitmusChaos will be made from master. Releases of
|
||||
LitmusChaos will be marked with GPG signed tags and announced at
|
||||
https://github.com/litmuschaos/litmus/releases. The tag will be of the
|
||||
format `<major>.<minor>.<patch>` and should be made with the command `git tag
|
||||
-s <major>.<minor>.<patch>`.
|
||||
|
||||
After a minor release, a branch will be created, with the format
|
||||
`release-<major>.<minor>.x` from the minor tag. All further patch releases will
|
||||
be done from that branch. For example, once we release `1.0.0`, a branch
|
||||
`release-1.0.x` will be created from that tag. All future patch releases will be
|
||||
done against that branch.
|
||||
|
||||
### Pre-releases
|
||||
|
||||
Pre-releases, such as alphas, betas and release candidates will be conducted
|
||||
from their source branch. For major and minor releases, these releases will be
|
||||
done from main. For patch releases, these pre-releases should be done within
|
||||
the corresponding release branch.
|
||||
|
||||
While pre-releases are done to assist in the stabilization process, no
|
||||
guarantees are provided.
|
||||
|
||||
### Upgrade Path
|
||||
|
||||
The upgrade path for LitmusChaos is such that the 0.0.x patch releases are
|
||||
always backward compatible with its major and minor version. Minor (0.x.0)
|
||||
version will always be compatible with the previous minor release. i.e. 1.2.0
|
||||
is backwards compatible with 1.1.0 and 1.1.0 is compatible with 1.0.0. There is
|
||||
no compatibility guarantees for upgrades that span multiple, _minor_ releases.
|
||||
For example, 1.0.0 to 1.2.0 is not supported. One should first upgrade to 1.1,
|
||||
then 1.2.
|
||||
|
||||
There are no compatibility guarantees with upgrades to _major_ versions. For
|
||||
example, upgrading from 1.0.0 to 2.0.0 may require resources to migrated or
|
||||
integrations to change. Each major version will be supported for at least 1
|
||||
year with bug fixes and security patches.
|
||||
|
||||
### Next Release
|
||||
|
||||
The activity for the next release will be tracked in the
|
||||
[project board](https://github.com/litmuschaos/litmus/projects). If your
|
||||
issue or PR is not present in the project board, please reach out to the maintainers or discuss the same on the #litmus-dev slack channel to create the milestone or add an issue or PR to an existing milestone.
|
||||
|
||||
### Support Horizon
|
||||
|
||||
Support horizons will be defined corresponding to a release branch, identified
|
||||
by `<major>.<minor>`. Release branches will be in one of several states:
|
||||
|
||||
- __*Next*__: The next planned release branch.
|
||||
- __*Active*__: The release is a stable branch which is currently supported and accepting patches.
|
||||
- __*Extended*__: The release branch is only accepting security patches.
|
||||
- __*LTS*__: The release is a long term stable branch which is currently supported and accepting patches.
|
||||
- __*End of Life*__: The release branch is no longer supported and no new patches will be accepted.
|
||||
|
||||
Releases will be supported at least one year after a _minor_ release. This means that
|
||||
we will accept bug reports and backports to release branches until the end of
|
||||
life date. If no new _minor_ release has been made, that release will be
|
||||
considered supported until 6 months after the next _minor_ is released or one year,
|
||||
whichever is longer. Additionally, releases may have an extended security support
|
||||
period after the end of the active period to accept security backports. This
|
||||
timeframe will be decided by maintainers before the end of the active status.
|
||||
|
||||
Long term stable (_LTS_) releases will be supported for at least three years after
|
||||
their initial _minor_ release. These branches will accept bug reports and
|
||||
backports until the end of life date. They may also accept a wider range of
|
||||
patches than non-_LTS_ releases to support the longer term maintainability of the
|
||||
branch, including library dependency, toolchain (including Go) and other version updates
|
||||
which are needed to ensure each release is built with fully supported dependencies and
|
||||
remains usable by LitmusChaos clients. _LTS_ releases can also accept feature backports
|
||||
to support new Kubernetes releases. The default action has to be reject it though,
|
||||
for long-term stability. This is still negotiable when the feature is a hard dependency
|
||||
for a new release of Kubernetes. There should be at least a 6-month overlap between
|
||||
the end of life of an _LTS_ release and the initial release of a new _LTS_ release.
|
||||
Up to 6 months before the announced end of life of an _LTS_ branch, the branch may
|
||||
convert to a regular _Active_ release with stricter backport criteria.
|
||||
|
||||
The current state is available in the following tables:
|
||||
|
||||
| Release | Status | Start | End of Life |
|
||||
| --------- | ------------- | ------------------ | ------------------- |
|
||||
| [0.x {0.6 - 0.9}](https://github.com/litmuschaos/litmus/releases/tag/0.9.0) | End of Life | Sept 13, 2019 | Jun 15, 2020 |
|
||||
| [1.x {1.0 - 1.13}](https://github.com/litmuschaos/litmus/releases/tag/1.13.8) | End of Life | Jan 8, 2020 | - |
|
||||
| [2.0 beta {2.0 beta 0 to 2.0 beta 9}](https://github.com/litmuschaos/litmus/releases/tag/2.0.0-Beta9) | End of Life | Mar 05, 2021 | July 15, 2021 |
|
||||
| [2.x](https://github.com/litmuschaos/litmus/releases/tag/2.14.0) | End of Life | Aug 9, 2021 | September 5, 2023 |
|
||||
| [3.x beta](https://github.com/litmuschaos/litmus/releases/tag/3.0.0-beta12) | End of Life | Nov 16, 2022 | Nov 15, 2023 |
|
||||
| [3.x](https://github.com/litmuschaos/litmus/releases/tag/3.6.1) | Active | Oct 04, 2023 | active(release of 4.0 + 6 months), |
|
||||
| [4.0](https://github.com/litmuschaos/litmus/blob/master/ROADMAP.md) | Next | TBD | TBD |
|
||||
|
||||
> **_NOTE_** LitmusChaos v3.x will end of life at the same time as v4.x reaches full stability. Due to
|
||||
> [Minimal Version Selection](https://go.dev/ref/mod#minimal-version-selection) used
|
||||
> by Go modules, 3.x must be supported until EOL of all 3.x releases. Once 3.x is in
|
||||
> extended support, it will continue to accept security patches in addition to client
|
||||
> changes relevant for package importers.
|
||||
|
||||
### Kubernetes Support
|
||||
|
||||
The Kubernetes version matrix represents the versions of LitmusChaos which are
|
||||
recommended for a Kubernetes release. Any actively supported version of
|
||||
LitmusChaos may receive patches to fix bugs encountered in any version of
|
||||
Kubernetes, however, our recommendation is based on which versions have been
|
||||
the most thoroughly tested. See the [Kubernetes test grid](https://testgrid.k8s.io/sig-node-LitmusChaos)
|
||||
for the list of actively tested versions. Kubernetes only supports n-3 minor
|
||||
release versions and LitmusChaos will ensure there is always a supported version
|
||||
of LitmusChaos for every supported version of Kubernetes.
|
||||
|
||||
| Kubernetes Version | LitmusChaos Version |
|
||||
|---------------------|---------------------|
|
||||
| 1.26 | 1.x, 2.x, 3.x |
|
||||
| 1.27 | 3.x |
|
||||
| 1.28 | 3.x |
|
||||
| 1.29 | 3.x |
|
||||
| 1.30 | 3.x, 4.0(wip) |
|
||||
|
||||
|
||||
## Public API Stability
|
||||
|
||||
The following table provides an overview of the components covered by
|
||||
LitmusChaos versions:
|
||||
|
||||
|
||||
| Component | Status | Stabilized Version | Links |
|
||||
|------------------|----------|--------------------|---------------|
|
||||
| GraphQL API | Stable | 1.0 | [graphql API]() |
|
||||
| Go client API | Stable | 2.0 | [godoc]() |
|
||||
|
||||
|
||||
From the version stated in the above table, that component must adhere to the
|
||||
stability constraints expected in release versions.
|
||||
|
||||
Unless explicitly stated here, components that are called out as unstable or
|
||||
not covered may change in a future minor version. Breaking changes to
|
||||
"unstable" components will be avoided in patch versions.
|
||||
|
||||
Go client API stability includes the `client`, `defaults` and `version` package
|
||||
as well as all packages under `pkg`, `core`, `api` and `protobuf`.
|
||||
All packages under `cmd`, `contrib`, `integration`, and `internal` are not
|
||||
considered part of the stable client API.
|
||||
|
||||
|
||||
|
||||
### Release Checklist
|
||||
|
||||
* [ ] Release branch creation on litmus component repos
|
||||
|
|
78
ROADMAP.md
78
ROADMAP.md
|
@ -1,6 +1,6 @@
|
|||
## LITMUS ROADMAP
|
||||
|
||||
This document captures only the high level roadmap items. For the detailed backlog, see [issues list](https://github.com/litmuschaos/litmus/issues) and [current milestones](https://github.com/litmuschaos/litmus/milestones).
|
||||
This document captures only the high level roadmap items. For the detailed backlog, see [issues list](https://github.com/litmuschaos/litmus/issues).
|
||||
|
||||
### Completed
|
||||
|
||||
|
@ -9,42 +9,64 @@ This document captures only the high level roadmap items. For the detailed backl
|
|||
- Off the shelf / ready chaos experiments for general Kubernetes chaos
|
||||
- Self sufficient, Centralized Hub for chaos experiments
|
||||
- Per-experiment minimal RBAC permissions definition
|
||||
- Helm3 charts for Litmus Chaos (operator, kubernetes/generic chaos charts)
|
||||
- Creation of 'scenarios' involving multiple faults via Argo-based Chaos Workflows (with examples for microservices apps like podtato-head and sock-shop)
|
||||
- Cross-Cloud Control Plane (Litmus Portal) to perform chaos against remote clusters
|
||||
- Helm charts for LitmusChaos control plane
|
||||
- Helm Chart for LitmusChaos execution Plane
|
||||
- Support for admin mode (centralized chaos management) as well as namespaced mode (multi-tenant clusters)
|
||||
- Generation of Kubernetes chaos events in experiments
|
||||
- Continuous chaos via flexible schedules, with support to halt/resume or (manual/conditional) abort experiments
|
||||
- Provide complete workflow termination/abort capability
|
||||
- Generation of observability data via Prometheus metrics and Kubernetes chaos events for experiments
|
||||
- Steady-State hypothesis validation before, during and after chaos injection via different probe types
|
||||
- Support for Docker, Containerd & CRI-O runtime
|
||||
- Scaffolding scripts (SDK) to help bootstrap a new chaos experiment in Go, Ansible
|
||||
- Continuous chaos via flexible scheduling policies, with support to halt/resume or abort experiments
|
||||
- Ability to customize/override experiment defaults on an instance basis
|
||||
- Support for scheduling policies (nodeSelector, tolerations) and resource definitions for chaos pods
|
||||
- ChaosHub refactor for 2.x user flow
|
||||
- Support for ARM64 nodes
|
||||
- Minimized role permissions for Chaos Service Accounts
|
||||
- Scaffolding scripts (SDK) to help bootstrap a new chaos experiment in Go, Python, Ansible
|
||||
- Support orchestration of non-native chaos libraries via the BYOC (Bring-Your-Own-Chaos) model
|
||||
- Define creation of scenarios involving multiple experiments via Argo-based Chaos Workflows
|
||||
- Support for OpenShift platform
|
||||
- Gitlab e2e pipeline for chaos experiments
|
||||
- Documentation (user & developer guides, integration with other chaos tools)
|
||||
- Add architecture details & design resources
|
||||
- Define community sync up schedule
|
||||
- Workflow YAML linter addition
|
||||
- Integration tests & e2e framework creation for control plane components and chaos experiments
|
||||
- Documentation (usage guide for chaos operator, resources & developer guide for new experiment creation)
|
||||
- Improved documentation and tutorials for Litmus Portal based execution flow
|
||||
- Add architecture details & design resources
|
||||
- Define community sync up cadence and structure
|
||||
|
||||
------
|
||||
|
||||
### In-Progress (Near-term)
|
||||
### In-Progress (Under Design OR Active Development)
|
||||
|
||||
- Improved runtime validation of chaos dependencies via litmus admission controllers
|
||||
- Support for Kubernetes pod scheduling policies (affinity rules for chaos resources)
|
||||
- A UI portal for LitmusChaos to trigger and schedule chaos experiments & workflows. Ongoing development [here](https://github.com/litmuschaos/litmus/tree/master/litmus-portal/)
|
||||
- Off the shelf chaos-integrated grafana dashboards for OpenEBS, Kafka, Cassandra [#1280](https://github.com/litmuschaos/litmus/issues/1280)
|
||||
- Support for user defined chaos experiment result definition (ex:json blob as chaos result) [#1254](https://github.com/litmuschaos/litmus/issues/1254)
|
||||
- Increased IO-Chaos libraries [#1623](https://github.com/litmuschaos/litmus/issues/1623)
|
||||
- HTTP Chaos libraries [#1179](https://github.com/litmuschaos/litmus/issues/1179)
|
||||
- Create and functionalize Special Interest Groups (SIGs) around specific areas in the project to take the roadmap forward
|
||||
- Native Chaos Workflows with redesigned subscriber to improve resource delegation, enabling seamless and efficient execution of chaos workflows within Kubernetes clusters.
|
||||
- Introduce transient runners to improve resource efficiency during chaos experiments by dynamically creating and cleaning up chaos runner instances.
|
||||
- Implement Kubernetes connectors to enable streamlined integration with Kubernetes clusters, providing simplified authentication and configuration management.
|
||||
- Integrate with tools like K8sGPT to generate insightful reports that identify potential weaknesses in your Kubernetes environment before executing chaos experiments.
|
||||
- Add Terraform support for defining and executing chaos experiments on infrastructure components, enabling infrastructure-as-code-based chaos engineering.
|
||||
- Add SDK support for Python and Java, with potential extensions to other programming languages based on community interest.
|
||||
- Include in-product documentation, such as tooltips, to improve user experience and ease of adoption.
|
||||
- Implement the litmus-java-sdk with a targeted v1.0.0 release by Q1.
|
||||
- Integrate distributed tracing by adding attributes or events to spans, and create an OpenTelemetry demo showcasing chaos engineering observability.
|
||||
- Enhance the exporter to function as an OpenTelemetry collector, providing compatibility with existing observability pipelines.
|
||||
- Add support for DocumentDB by replacing certain MongoDB operations, improving flexibility for database chaos.
|
||||
- Upgrade Kubernetes SDK from version 1.21 to 1.26 to stay aligned with the latest Kubernetes features and enhancements.
|
||||
- Refactor the chaos charts to:
|
||||
- Replace latest tags with specific, versioned image tags.
|
||||
- Consolidate multiple images into a single optimized image.
|
||||
- Update GraphQL and authentication API documentation for improved clarity and user guidance.
|
||||
- Add comprehensive unit and fuzz tests to enhance code reliability and robustness.
|
||||
- Implement out-of-the-box Slack integration for better collaboration and monitoring during chaos experiments.
|
||||
|
||||
------
|
||||
|
||||
### Backlog
|
||||
### Backlog
|
||||
|
||||
- Add pre-defined chaos workflows for the [podtato-head](https://github.com/cncf/podtato-head) model app from CNCF Ap-Delivery SIG
|
||||
- Pre-defined chaos workflows to inject chaos during application benchmark runs
|
||||
- Support for cloudevents compliant chaos events
|
||||
- Increased chaos metrics via prometheus chaos exporter
|
||||
- Migration to native Kubernetes ansible modules for ansible-based experiments
|
||||
- Improved application Chaos Suites (OpenEBS, Kafka, Cassandra)
|
||||
- Support for platform (AWS, GKE, vSphere) Chaos
|
||||
- Validation support for all ChaosEngine schema elements within workflow wizard
|
||||
- Chaos-center users account to chaosService account map
|
||||
- Cross-hub experiment support within a Chaos Workflow
|
||||
- Enhanced CRD schema for ChaosEngine to support advanced CommandProbe configuration
|
||||
- Support for S3 artifact sink (helps performance/benchmark runs)
|
||||
- Chaos experiments against virtual machines and cloud infrastructure (AWS, GCP, Azure, VMWare, Baremetal)
|
||||
- Off the shelf chaos-integrated monitoring dashboards for application chaos categories
|
||||
- Support for user defined chaos experiment result definition
|
||||
- Increased fault injection types (IOChaos, HTTPChaos, JVMChaos)
|
||||
- Special Interest Groups (SIGs) around specific areas in the project to take the roadmap forward
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
# SECURITY
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We are extremely grateful for security researchers and users that report vulnerabilities to the LitmusChaos Open Source Community. All reports are thoroughly investigated by a set of community members.
|
||||
|
||||
To report a litmuschaos vulnerability, either:
|
||||
|
||||
1. Report it on Github directly:
|
||||
|
||||
Navigate to the security tab on the repository
|
||||

|
||||
|
||||
Click on 'Advisories'
|
||||

|
||||
|
||||
Click on 'Report a vulnerability'
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
2. Send an email to `litmuschaos@gmail.com` detailing the issue and steps
|
||||
to reproduce.
|
||||
|
||||
The reporter(s) can expect a response within 24 hours acknowledging
|
||||
the issue was received. If a response is not received within 24 hours, please
|
||||
reach out to any committer directly to confirm receipt of the issue.
|
||||
|
||||
|
||||
To make a report, submit your vulnerability to all security contacts of LitmusChaos [listed below](#security-contacts). This allows triage and handling of the vulnerability with standardized response times.
|
||||
|
||||
### When Should I Report a Vulnerability?
|
||||
|
||||
- You think you discovered a potential security vulnerability in LitmusChaos
|
||||
- You are unsure how a vulnerability affects LitmusChaos
|
||||
- You think you discovered a vulnerability in another project that LitmusChaos depends on. For projects with their own vulnerability reporting and disclosure process, please report it directly there.
|
||||
|
||||
### When Should I NOT Report a Vulnerability?
|
||||
|
||||
- You need help tuning LitmusChaos components for security - please discuss this is in the various LitmusChaos community channels
|
||||
- You need help applying security-related updates
|
||||
- Your issue is not security-related
|
||||
|
||||
|
||||
## Review Process
|
||||
|
||||
Once a committer has confirmed the relevance of the report, a draft security
|
||||
advisory will be created on Github. The draft advisory will be used to discuss
|
||||
the issue with committers, the reporter(s), and litmuschaos's security advisors.
|
||||
If the reporter(s) wishes to participate in this discussion, then provide
|
||||
reporter Github username(s) to be invited to the discussion. If the reporter(s)
|
||||
does not wish to participate directly in the discussion, then the reporter(s)
|
||||
can request to be updated regularly via email.
|
||||
|
||||
If the vulnerability is accepted, a timeline for developing a patch, public
|
||||
disclosure, and patch release will be determined. If there is an embargo period
|
||||
on public disclosure before the patch release, an announcment will be sent to
|
||||
the security announce mailing list announcing the scope of the vulnerability, the date of availability of the
|
||||
patch release, and the date of public disclosure. The reporter(s) are expected
|
||||
to participate in the discussion of the timeline and abide by agreed upon dates
|
||||
for public disclosure.
|
||||
|
||||
## Security Vulnerability Response
|
||||
|
||||
Each report is acknowledged and analyzed by the security contacts within 5 working days. This will set off the [Security Release Process](#process).
|
||||
|
||||
Any vulnerability information shared with the LitmusChaos security contacts stays within LitmusChaos project and will not be disseminated to other projects unless it is necessary to get the issue fixed.
|
||||
|
||||
## Public Disclosure Timing
|
||||
|
||||
A public disclosure date is negotiated by the LitmusChaos Security Committee and the bug submitter. We prefer to fully disclose the bug as soon as possible once a user mitigation is available. It is reasonable to delay disclosure when the bug or the fix is not yet fully understood, the solution is not well-tested, or for vendor coordination. The timeframe for disclosure is from immediate (especially if it is already publicly known) to a few weeks. For a vulnerability with a straightforward mitigation, we expect report date to disclosure date to be on the order of 7 days. The LitmusChaos Security Committee holds the final say when setting a disclosure date.
|
||||
|
||||
## Process
|
||||
|
||||
If you find a security-related bug in LitmusChaos, we kindly ask you for responsible disclosure and for giving us appropriate time to react, analyze, and develop a fix to mitigate the found security vulnerability. The security contact will investigate the issue within 5 working days.
|
||||
|
||||
The team will react promptly to fix the security issue and its workaround/fix will be published on our release notes.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
See the [litmuschaos release page]()
|
||||
for information on supported versions of litmuschaos. Any `Extended` or `Active`
|
||||
release branch may receive security updates. For any security issues discovered
|
||||
on older versions, non-core packages, or dependencies, please inform committers
|
||||
using the same security mailing list as for reporting vulnerabilities.
|
||||
|
||||
## Joining the security announce mailing list
|
||||
|
||||
Any organization or individual who directly uses litmuschaos and non-core
|
||||
packages in production or in a security critical application is eligible to join
|
||||
the security announce mailing list. Indirect users who use litmuschaos through a
|
||||
vendor are not expected to join, but should request their vendor join. To join
|
||||
the mailing list, the individual or organization must be sponsored by either a
|
||||
litmuschaos committer or security advisor as well as have a record of properly
|
||||
handling non-public security information. If a sponsor cannot be found,
|
||||
sponsorship may be requested at `litmuschaos@gmail.com`. Sponsorship should not
|
||||
be requested via public channels since membership of the security announce list
|
||||
is not public.
|
||||
|
||||
|
||||
## Security Vulnerability Response
|
||||
|
||||
Each report is acknowledged and analyzed by the security contacts within 5 working days. This will set off the [Security Release Process](#process).
|
||||
|
||||
Any vulnerability information shared with the LitmusChaos security contacts stays within LitmusChaos project and will not be disseminated to other projects unless it is necessary to get the issue fixed.
|
||||
|
||||
## Public Disclosure Timing
|
||||
|
||||
A public disclosure date is negotiated by the LitmusChaos Security Committee and the bug submitter. We prefer to fully disclose the bug as soon as possible once a user mitigation is available. It is reasonable to delay disclosure when the bug or the fix is not yet fully understood, the solution is not well-tested, or for vendor coordination. The timeframe for disclosure is from immediate (especially if it is already publicly known) to a few weeks. For a vulnerability with a straightforward mitigation, we expect report date to disclosure date to be on the order of 7 days. The LitmusChaos Security Committee holds the final say when setting a disclosure date.
|
||||
|
||||
## Process
|
||||
|
||||
If you find a security-related bug in LitmusChaos, we kindly ask you for responsible disclosure and for giving us appropriate time to react, analyze, and develop a fix to mitigate the found security vulnerability. The security contact will investigate the issue within 5 working days.
|
||||
|
||||
The team will react promptly to fix the security issue and its workaround/fix will be published on our release notes.
|
||||
|
||||
## Security Contacts
|
||||
|
||||
Defined below are the security contacts for this repository. In case you identify any security issue, please reach out to all of the security contacts.
|
||||
|
||||
- @ksatchit (karthik satchitanand, karthik.s@harness.io)
|
||||
- @rajdas98 (raj babu das, raj.das@harness.io)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# LitmusChaos Training and Courses
|
||||
|
||||
Here is a list of training and courses related to LitmusChaos available out there. LitmusChaos is a CNCF project which does not endorse any particular course.
|
||||
If you have created a training or course for LitmusChaos and wish to add the same, please raise a PR against this document.
|
||||
|
||||
|
||||
- [Harness Chaos Engineering Practitioner](https://university.harness.io/path/harness-chaos-engineering-practitioner)
|
||||
- [Configuring Kubernetes for Reliability with LitmusChaos](https://www.udemy.com/course/configuring-kubernetes-for-reliability-with-litmuschaos/)
|
|
@ -0,0 +1,13 @@
|
|||
This is a list of users that are using & benefited by LitmusChaos. Please send a PR to this file (along with details [here](./adopters/users))
|
||||
to add yourselves.
|
||||
|
||||
|
||||
| User | Usecase | Details |
|
||||
| :--- | :--- | :--- |
|
||||
| [Laura Henning](https://github.com/LaumiH)|Reasearch on how to do chaos engineering in minikube clusters like [these](https://github.com/LaumiH/k8sstuff)|[My Story](adopters/users/Laura_Henning.md) |
|
||||
| [Johnny Jacob](https://github.com/johnnyjacob)|Testing deployment designs for resiliency|Coming Soon!|
|
||||
| [Jayesh Kumar Tank](https://github.com/k8s-dev)|Create Cloud Native Validation Suite on [Microservices Application](https://github.com/k8s-dev/microservices-demo)|[My Story](adopters/users/Jayesh_Kumar_Tank.md)|
|
||||
| [Bhaumik Shah](https://github.com/Bhaumik1802)|Use LitmusChaos for Kafka Resiliency on Dev/Staging|[My Story](adopters/users/Bhaumik_Shah.md)|
|
||||
| [Jayadeep KM](https://github.com/kmjayadeep)|Ensure reliability of microservices|[My Story](adopters/users/Jayadeep_KM.md)|
|
||||
| [Shantanu Deshpande](https://github.com/ishantanu)|Chaos Engineering Practice as SRE|[My Story](adopters/users/Shantanu_Deshpande.md)|
|
||||
| [Omar Hanafi](https://github.com/oHanafi)|Performance Anomaly Detection in Cloud and Containerized Applications|Coming Soon!|
|
|
@ -0,0 +1,15 @@
|
|||
## AB-Inbev
|
||||
|
||||
Anheuser-Busch InBev SA/NV, commonly known as AB InBev, is a Belgian multinational drink and brewing company based in Leuven, Belgium. AB InBev has a global functional management office in New York City, and regional headquarters in São Paulo, London, St. Louis, Mexico City, Bremen, Johannesburg and others.
|
||||
|
||||
## Why Do We Use Litmus:
|
||||
|
||||
After an evaluation period of some Chaos Engineering tools, we chose Litmus because it is a more mature tool that would meet most of our needs. We are in the implementation, configuration, and process definition phase.
|
||||
AB-Inbev's BEES is a huge project that has hundreds of microservices, it has been a great challenge to adapt Litmus in this process, making customizations and counting on the help of the Litmus community to evolve the tool and thus achieve our goal of making it available to the teams.
|
||||
Some points that made us choose Litmus:
|
||||
|
||||
- Based on K8S resources
|
||||
- SSO
|
||||
- Customization of attacks, attacks in parallel
|
||||
- Installation on multiple clusters
|
||||
- GitOps
|
|
@ -0,0 +1,29 @@
|
|||
## Adidas
|
||||
[Adidas](https://adidas.com) is a German multinational corporation, founded and headquartered in Herzogenaurach, Bavaria, that designs and manufactures shoes, clothing and accessories.
|
||||
|
||||
|
||||
## Why do we use Litmus.
|
||||
|
||||
In adidas, we started months ago with a new initiative about how to implement chaos engineering practices in order to provide the engineering teams a guide and tools about how to test the resilience of the applications through chaos engineering. With this goal in mind, we started to define some best practices and processes to be shared with our engineering team, and we started to evaluate a few tools.
|
||||
|
||||
After an evaluation of different tools, we decided to go ahead with Litmus Chaos.
|
||||
|
||||
## How are we using Litmus chaos:
|
||||
Applications/Workloads or Infra that are being subjected to chaos by Litmus
|
||||
|
||||
- Litmus chaos will be provided by our platform team as part of their services. It will be running on kubernetes and will be available for engineering teams.
|
||||
- Experiments, like pod deletion, network latency or packetloss, applied between functional dependencies like checkout & Payments, login, order creation...
|
||||
- Not applied in production yet.
|
||||
|
||||
## Why was Litmus chosen & How it is helping you
|
||||
We defined a set of priorities (with different value) and stoppers, we analyzed the tooling and selected the most valued one:
|
||||
|
||||
- Prio 1 & Stoppers if not: Full detailed documentation in English available, API / Shared Libraries, Control Injecting Failure, Permissions scope isolated, Authorization, chaos Scenarios - Parallel, works with: Kuberentes, OpenSource
|
||||
- Prio 2: Installation and Management, Metrics / Reporting, Halt attack, Automatic rollback, High/admin permissions on the node, Chaos scenarios as code, chaos attacks - Serial, Custom or Specialized Attacks, Custom or Specialized Scenarios, Works with: AWS
|
||||
- Prio 3: Access to the logs, Scheduling attacks, Health Checks, Application Attacks, Target Randomization, Network Attacks, VMs Attacks, Public API, Web UI
|
||||
|
||||
## How do we use Litmus
|
||||
- Staging/pre-prod
|
||||
- Planned to go to production and through CI/CD pipelines.
|
||||
If you would like your name (as standalone user) or organization name to be added to the Adopters.md, please provide a preferred contact handle like GitHub id, Twitter id, LinkedIn id, website etc.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Amadeus
|
||||
[Amadeus](https://amadeus.com/) technology powers the global travel and tourism industry.
|
||||
|
||||
From airlines to search engines, travel agencies to hotels, the world's top travel brands rely on Amadeus to help create exceptional traveler experiences.
|
||||
|
||||
## How do we use Litmus.
|
||||
We are using Litmus for the following 3 topics:
|
||||
- **Identify weakness** by injecting a wide variety of disruptions to catch bugs and gaps in the stability of our applications
|
||||
- **Build confidence in the resiliency** by introducing disruptions that activate our resiliency mechanisms to ensure they are working as expected.
|
||||
- **Validate fixes** by recreating specific conditions and disruptions, we can reproduce complex production incidents and validate the fixes deployed to resolve them.
|
||||
|
||||
## Benefits in using Litmus.
|
||||
|
||||
We are finding the following benefits in Litmus
|
||||
- **Open Source**: Allows us to contribute new features and fix bugs based on feedback from our Chaos users.
|
||||
- **QA-Friendly**: Through the UI and YAML-based configuration, it allows QA profiles with limited SRE knowledge to easily create their own experiments.
|
||||
- **Extensibility**: As Chaos Scenarios are based on ArgoWorkflow, it allows for the inclusion of custom steps, such as updating a configuration before/after the experiments.
|
||||
- **Variety of Disruption Types**: Which satisfies our current Chaos users.
|
|
@ -0,0 +1,16 @@
|
|||
# APISIX Apache
|
||||
|
||||
Developed and donated by API7.ai, Apache APISIX is an open source, dynamic, scalable, and high-performance cloud native API gateway for all your APIs and microservices. It is a top-level project of the Apache Software Foundation.
|
||||
|
||||
You can use APISIX API Gateway as a traffic entrance to process all business data. It offers features including dynamic routing, dynamic upstream, dynamic certificates, A/B testing, canary release, blue-green deployment, limit rate, defense against malicious attacks, metrics, monitoring alarms, service observability, service governance, and more.
|
||||
|
||||
|
||||
|
||||
|
||||
## How do we use Litmus
|
||||
We practice chaos engineering using Litmus in the Apache APISIX Ingress.
|
||||
|
||||
Litmus also helped us find hidden bugs.
|
||||
|
||||
Project website: https://apisix.apache.org/
|
||||
This is the text version of my online sharing content. https://dev.to/apisix/building-a-more-robust-apache-apisix-ingress-controller-with-litmus-chaos-3ldn
|
|
@ -0,0 +1,14 @@
|
|||
## CI&T
|
||||
CI&T is an information technology and software engineering company. CI&T operates as a global systems integrator headquartered in the Brazilian city of Campinas with offices in other regions of South America, United States, Europe, and Asia.
|
||||
|
||||
## Why Do We Use Litmus:
|
||||
|
||||
After an evaluation period of some Chaos Engineering tools, we chose Litmus because it is a more mature tool that would meet most of our needs. We are in the implementation, configuration, and process definition phase.
|
||||
AB-Inbev's BEES is a huge project that has hundreds of microservices, it has been a great challenge to adapt Litmus in this process, making customizations and counting on the help of the Litmus community to evolve the tool and thus achieve our goal of making it available to the teams.
|
||||
Some points that made us choose Litmus:
|
||||
|
||||
- Based on K8S resources
|
||||
- SSO
|
||||
- Customization of attacks, attacks in parallel
|
||||
- Installation on multiple clusters
|
||||
- GitOps
|
|
@ -0,0 +1,13 @@
|
|||
CNF Test Suite
|
||||
---
|
||||
|
||||
The [CNF Test Suite](https://github.com/cncf/cnf-testsuite) is an open source test suite for Cloud Native Network Function (CNF) developers and network operators to evaluate how well a telecom service (a platform or network application, aka CNF) follows cloud native principles and best practices, like resilience.
|
||||
|
||||
## Why do we use Litmus
|
||||
Subjecting the telecom services to chaos testing is useful in finding failure points and suggesting remediation steps toward improving resilience. Therefore, we chose LitmusChaos to create resilience tests in the CNF Test Suite.
|
||||
|
||||
## How do we use Litmus
|
||||
By including LitmusChaos experiments in the CNF Test Suite's workload tests, we are able to run telecom services in resilience experiments including: **pod-network-duplication**, **pod-network-corruption**, **pod-io-stress**, **pod-memory-hog**, **pod-delete**, **disk-fill**, **pod-network-latency**, and more. This helps the end user see how their service behaves when exposed to common application failures.
|
||||
|
||||
## Benefits in using Litmus
|
||||
The benefits we see in LitmusChaos are: it is part of the CNCF ecosystem, it is designed for Kubernetes workloads, it has a vibrant community and it is well maintained.
|
|
@ -0,0 +1,12 @@
|
|||
## Container Solutions
|
||||
[Container Solutions](https://www.container-solutions.com/) We bring culture, strategy, and technology together —to make sure your Cloud Native transformation is done right.
|
||||
|
||||
## How do we use Litmus
|
||||
We have used Litmus to build out Chaos Engineering platforms with some of our large E-Commerce customers to improve resilience for big sales periods such as Black Friday.
|
||||
|
||||
We looked into quite a few tools, and Litmus provided us with the flexibility we needed, whilst bootstrapping many of the components we would have to write ourselves.
|
||||
|
||||
We also used Litmus Chaos experiments when discussing some of our customer's architecture constraints, and showing them real world cases of how to make Kubernetes more resilient.
|
||||
|
||||
One concrete use case was our customer wanting to build a cluster per app, whilst we wanted to build bigger clusters for easier management. We would use Litmus to show what application failure looks like on one part of the cluster, and show global resilience in their cluster when this happens.
|
||||
The Litmus community and *product have been a great addition to our tool stack, and provided many benefits for us.
|
|
@ -0,0 +1,13 @@
|
|||
## Emirates NBD
|
||||
|
||||
[Emirates NBD](https://www.emiratesnbd.com) is Dubai's government-owned bank and is one of the largest banking groups in the Middle East in terms of assets.
|
||||
|
||||
### **Why do we use Litmus.**
|
||||
|
||||
Resilience is a key aspect in creating fault-tolerant environments, and leveraging tools like Litmus has been instrumental in automating resilience testing. Litmus has enabled us to simulate real-time chaos scenarios, allowing us to thoroughly verify the robustness of both our infrastructure and applications.
|
||||
|
||||
### **How do we use Litmus.**
|
||||
|
||||
We began with a proof of concept (POC) on a playground cluster. While we explored other tools during this process, Litmus stood out significantly, not only in its capabilities but also due to its excellent user interface. Although we faced a few challenges during the initial setup of Litmus on OpenShift, the team provided timely support, helping us overcome these obstacles and successfully complete the POC.
|
||||
|
||||
Now, we've successfully deployed Litmus in a non-production cluster environment, and our SRE team is in the process of transitioning from manual chaos testing to automated chaos tests. This shift will enable us to schedule, automate, and efficiently track the outcomes of these tests, enhancing the resilience of our systems.
|
|
@ -0,0 +1,20 @@
|
|||
## FIS
|
||||
[FIS](https://www.fisglobal.com/) is an American multinational corporation which offers a wide range of financial products and services.
|
||||
|
||||
## Why do we use Litmus.
|
||||
We at FIS Global, have been embarking on to larger SRE program to transform platform teams from purely operations focused to bring in SRE/Automation culture and mindset. As part of that larger effort, Chaos/Resiliency Engineering is identified as key program to improve stability and availability thus improve overall reliability of applications across organization and provide superior customer experience. We have chosen Litmus as a Chaos Engineering Tool because, It
|
||||
|
||||
Fulfills all of resiliency testing requirements
|
||||
Has good and responsive community
|
||||
Has good documentation
|
||||
is built on loosely coupled architecture
|
||||
Has nice dashboard features
|
||||
Exposes APIs to integrate with CI/CD pipelines
|
||||
|
||||
## Where we are using Litmus
|
||||
|
||||
Currently, using in Applications/Workloads but idea is to expand to Infrastructure, e.g. using network latency to identify and understand resiliency of upstream application/component when downstream application/component is slow, Use Pod delete under production load to understand the application's ability to self heal.
|
||||
Simulate experiments using Litmus to understand utilization of JVM's key resources such as thread pool, connection pool, heap memory etc
|
||||
Kafka Resiliency : Kafka itself is a complex distributed architecture solution, planning to use Litmus network and memory hog experiments to simulate latency between Producer and Broker, Consumer and Broker, Leader and Follower, and also trying to understand how cluster behaves under Memory and CPU pressure.
|
||||
Integrate Litmus with CI/CD over APIs so that Chaos Testing can be autonomous
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
## **Halodoc**
|
||||
|
||||
[Halodoc](https://www.halodoc.com/) is a secure health-tech platform with a mission to simplifying access to healthcare by connecting millions of patients with licensed doctors, insurance, labs, and pharmacies in one simple mobile application. Halodoc’s innovative technology, nimble services, and patient focus enable a host of solutions including 24/7 doctor tele consultation via chat, voice or video; medicine purchase & delivery; lab services at home; and strong customer support.
|
||||
|
||||
Halodoc is the 2018 Forbes Indonesia Choice Award winner and Galen Growth’s 2018 Most Innovative HealthTech Startup in Asia, a testimony to a team of compassionate, innovative, trustworthy and agile people who take ownership of their work in building the most trusted digital healthcare company.
|
||||
|
||||
### **Why we explored litmus**
|
||||
|
||||
We wanted a tool that we could leverage to test the resiliency of multi k8s cluster workloads in a private cloud,
|
||||
after evaluating different tools of similar flavour, we wanted to exercise our chaos experiments using Litmus, as it has the following benefits.
|
||||
|
||||
- It is designed for Kubernetes workload
|
||||
- It has the wide range of chaos experiments to perform on k8s workload and Infrastructure
|
||||
- It has litmus portal as control plane to target chaos experiments against multiple k8s cluster within our organisation.
|
||||
- It has prometheus integration, able to see in litmus portal dashboard about how each of the workflow chaos experiments perform.
|
||||
- It fits into our gitops flow to enable end to end automation.
|
||||
|
||||
|
||||
|
||||
### How we use litmus
|
||||
|
||||
At Halodoc we use Litmus to validate the resiliency of our private aws managed eks by covering the following areas.
|
||||
|
||||
- Testing resiliency of Control plane Kubernetes Infrastructure
|
||||
- Validating the HA of different control plane services
|
||||
- Target SRE tools at k8s clusters, benchmark it based on several parameters.
|
||||
|
||||
|
||||
|
||||
### Benefits of litmus
|
||||
|
||||
Litmus has has a wide variety of chaos experiments for Kubernetes workload and Infrastructure and provides a very easy way for end-to-end automation of resiliency test cases.
|
|
@ -0,0 +1,6 @@
|
|||
## iFood
|
||||
[iFood](https://ifood.com.br) is a Brazilian online food ordering and food delivery platform. It operates mainly in Brazil and Mexico, after it merged its businesses in Argentina and Colombia with rival PedidosYa.
|
||||
|
||||
## How are we using Litmus
|
||||
We have been using Litmus 2.X at iFood for a couple of months, replacing chaostoolkit as it provides a wider range of experiments out-of-the-box. We've started using it to validate the fallback mechanisms of critical services monthly. Right now, we are expanding its usage to go further and inject failures to drop access to databases, redis, Kafka and AWS services and learn from it and take some countermeasures to improve the critical services.
|
||||
I hope Litmus to become the de-facto tool to implement Chaos Engineering in a simple manner.
|
|
@ -0,0 +1,23 @@
|
|||
# Infracloud Technologies
|
||||
|
||||
InfraCloud Technologies is a Kubernetes focused B2B Open Source Cloud Native Computing company which has been building products, services, and solutions to modernize applications and infrastructure.
|
||||
|
||||
InfraCloud was one of the first Kubernetes partners and have been contributing to the open source community around cloud-native technologies. It has been growing almost 100% for last few years consistently.
|
||||
|
||||
Company website: https://www.infracloud.io/
|
||||
Company GitHub: https://github.com/infracloudio
|
||||
|
||||
## Why Do We Use Litmus:
|
||||
|
||||
At InfraCloud, we are using Litmus to develop Resiliency Frameworks.
|
||||
To simulate various Chaos scenarios using fault injection templates provided by Litmus. Litmus also helps to incorporate custom fault templates developed using AWS SSM documents.
|
||||
|
||||
## How do we use Litmus.
|
||||
Currently, we have tested with different kind of scenarios including faults like pod deletion, network latency, resource stressing, network partitioning in databases, and many more.
|
||||
|
||||
## Benefits in using Litmus.
|
||||
|
||||
Easy deployment.
|
||||
Easy Fault injection.
|
||||
Custom Grading for experiments
|
||||
SSM integration helps to inject fault in both EKS and external AWS components.
|
|
@ -0,0 +1,26 @@
|
|||
# **Klanik**
|
||||
|
||||
Founded in December 2011, [Klanik](https://www.klanik.com/en/atypik-company-2/#histoire) has built its success on its award-winning Consultant-Centric approach, defining us as a great place to work. #Happyatwork2021 This model is materialized by unprecedented personal and professional development programs:
|
||||
|
||||
## **Why we explored litmus**
|
||||
|
||||
We wanted a tool that we could leverage to test the containerized control plane of our clients public, private & hybrid cloud.
|
||||
When we came across Litmus, we wanted to give it a try.
|
||||
|
||||
... To be continued ;)
|
||||
<!-- - It is designed for Kubernetes workload
|
||||
- It has wide variety of generic test cases for Kubernetes workload and Infrastructure
|
||||
- It can be used to trigger customized validations
|
||||
- It is easy to Integrate with our existing automation framework
|
||||
|
||||
## How we explored litmus
|
||||
|
||||
We explored Litmus to validate the resiliency of our private telco cloud by covering the following areas.
|
||||
|
||||
- Testing resiliency of Control plane Kubernetes Infrastructure
|
||||
- Validating the HA of different control plane services
|
||||
- Testing inter dependency among different open source applications
|
||||
|
||||
## Benefits of litmus
|
||||
|
||||
Litmus has has a wide variety of generic test cases for Kubernetes workload and Infrastructure and provides a very easy way for end-to-end automation of resiliency test cases. -->
|
|
@ -0,0 +1,32 @@
|
|||
## Lenskart
|
||||
[Lenskart](https://www.lenskart.com) is one of the premium eyewear companies with a presence in the retail sector. [Lenskart](https://www.lenskart.com) has a
|
||||
lot of flagship products some of them like BluO and airflex which are very popular among customers. [Lenskart](https://www.lenskart.com) also has subsidiary
|
||||
retail eyewear companies like [John&Jacobs](https://www.johnjacobseyewear.com/) and [Aqualens](https://aqualens.in/). [Lenskart](https://www.lenskart.com)
|
||||
is not only a retail shop but has an omnichannel business model which serves customer from online and offline stores spread not only across India but also
|
||||
expanding gloablly.
|
||||
|
||||
## **Motivation**
|
||||
|
||||
[Lenskart](https://www.lenskart.com) not only serves fashion products to customer but also power eyeglasses and contact lenses which are custom made for every
|
||||
customer. This makes it a very niche product as well as an company serving essentials commodities for it's customer. Delivery and ease of ordering from online
|
||||
and offline stores is what we take very seriously. Any downtime could cost not only business but also could impact delivery of his/her eyeglasses or contact
|
||||
lenses. As an engineering team who is always accepting challenges , we started looking at chaos engineering very seriously once we also faced some major
|
||||
downtimes in our platform due to system failure or DDOS attacks. It didn't take us time to realize that just running shop on the cloud was not good enough
|
||||
we have to be prepared for chaotic situations, not just that, we also have to simulate it and find weak points in our architecture. So, we first started with manual
|
||||
and scripted chaos, but the problem was that they were hard to reproduce and involved a lot of effort to plan and execute them. Then we started exploring
|
||||
if we could have a framework which could help us maintain our chaos experiments in form of templates. After looking at a couple of tools we narrowed down on
|
||||
Litmus.
|
||||
|
||||
## **How are we using Litmus**
|
||||
|
||||
We started using Litmus from our devops kubernetes cluster , where we first started to test stateful service like redis cluster or elasticsearch. We then
|
||||
gradually started testing our own application services using Litmus. We have gradually moved from our integration environments to pre-production. We are regularly maturing
|
||||
our hypothesis and writing relevant experiments for these services. Currently, we haven't integrated these experiments into our CI/CD pipelines but in the future we have
|
||||
plans to run these experiments with every release.
|
||||
|
||||
## **Benefits of using litmus**
|
||||
|
||||
We are quite new to Litmus framework but what we have really gained from Litmus is templating of our chaos experiments and able to maintain them in our CVS.
|
||||
This has really helped us with the reproducibility of the experiments. It has opened new opportunities for us where we can write our own custom experiments
|
||||
which might be very specific targetting our in-house and public services. We have started ranking the experiments and adding them to our experiment suite so that we
|
||||
could now measure the reasiliency of our services. This would also help us in future to be more targeted in our resiliency journey.
|
|
@ -0,0 +1,12 @@
|
|||
## Mercedes
|
||||
|
||||
[Mercedes](https://www.mercedes-benz.com/) is among the leading automobile manufacturing company.
|
||||
|
||||
### **Why do we use Litmus.**
|
||||
To ensure resilience, detect bugs and test rollouts. We are still in the early stages.
|
||||
|
||||
### **How do we use Litmus.**
|
||||
Litmus is being used as part of dev/test cycles to catch bugs & verify resiliency.
|
||||
|
||||
### **Benefits in using Litmus.**
|
||||
The litmus is easy to use and extend/develop based on custom requirements and well-supported open source tool.
|
|
@ -0,0 +1,16 @@
|
|||
### **NEUDESIC**
|
||||
|
||||
[NEUDESIC](https://www.neudesic.com/) has assembled a fully-managed partner ecosystem of leading cloud and independent software partners that provide us maximum flexibility to architect technology, strategies and solutions that drive business growth. Neudesic offers decades of experience, proven frameworks and a disciplined approach to quickly deliver reliable, quality solutions that help you go to market faster and get a leg up on your competition.
|
||||
|
||||
|
||||
### Why do we use litmus
|
||||
|
||||
We are using litmus chaos to inject faults in our aks environments. Before arriving at litmus we explored other tools , but found litmus to be the most well rounded one and the one that aligned closest to the principles of chaos
|
||||
|
||||
### How do we use litmus
|
||||
|
||||
We are using litmus in our pre prod environments in the ci cd stage as a gate for releases
|
||||
|
||||
### Benefits in using Litmus
|
||||
|
||||
The chaos gated deployments make use of the in-built git ops integration in litmus
|
|
@ -0,0 +1,30 @@
|
|||
## OutSystems
|
||||
|
||||
[OutSystems](https://www.outsystems.com/) is a low-code development platform which provides tools for companies to develop, deploy and manage omnichannel enterprise applications. OutSystems was founded in 2001 in Lisbon, Portugal. In June 2018 OutSystems secured a $360M round of funding from KKR and Goldman Sachs and reached the status of Unicorn.
|
||||
|
||||
### **Leveraging Litmus Chaos Engineering in Kubernetes Infrastructure:**
|
||||
|
||||
We have a Kubernetes-based infrastructure pivotal to our operations, where reliability and resilience are paramount. Recognizing the need for robust testing methodologies, we turned to Litmus Chaos Engineering to fortify our systems against potential failures and to ensure seamless operations even under adverse conditions.
|
||||
|
||||
### **Why do we use Litmus:**
|
||||
|
||||
Litmus emerged as our tool of choice due to its comprehensive suite of chaos engineering capabilities tailored specifically for Kubernetes environments. Its versatility in orchestrating controlled chaos experiments aligns perfectly with our commitment to enhancing system reliability while maintaining agility.
|
||||
|
||||
### **Use Case and Implementation:**
|
||||
|
||||
We have seamlessly integrated Litmus Chaos Engineering into various stages of our development and deployment pipeline, spanning from development and testing to staging and production environments. Leveraging Litmus, we meticulously craft and execute chaos experiments, meticulously observing how our infrastructure behaves under stress, and ensuring it meets our predefined Service Level Objectives (SLOs) and Service Level Indicators (SLIs).
|
||||
|
||||
### **Achievements:**
|
||||
|
||||
Our journey with Litmus Chaos Engineering has been marked by significant milestones:
|
||||
|
||||
- Successful deployment of Chaos Center and Litmus Delegate, empowering us with centralized chaos management capabilities.
|
||||
- Establishment of secure access to Chaos Center through HTTPS, coupled with domain customization for enhanced usability.
|
||||
- Implementation of WAF ACL to restrict access to Chaos Center, ensuring secure interactions.
|
||||
- Integration of Azure SSO for streamlined user management and authentication.
|
||||
- Seamless connectivity between Chaos Center and target nodes, facilitating efficient chaos experimentation.
|
||||
- Execution of numerous successful experiments, validating the resilience and scalability of our infrastructure.
|
||||
|
||||
### **Next Steps:**
|
||||
|
||||
As we continue to harness the power of Litmus Chaos Engineering, we remain committed to expanding our chaos engineering initiatives, further refining our chaos experiments, and continually enhancing the resilience of our Kubernetes infrastructure.
|
|
@ -0,0 +1,32 @@
|
|||
## PokerBaazi
|
||||
|
||||
[PokerBaazi](https://www.pokerbaazi.com/) is India's biggest online poker platform providing an unparalleled world-class experience. Home Grown and 8 years of calling it our own, today, we have a strong and loyal user base of 40 LAC+ Indians.
|
||||
|
||||
### **Applications/Workloads or Infra that are being subjected to chaos by Litmus.**
|
||||
|
||||
At PokerBaazi, we leverage Litmus Chaos to subject critical components of our infrastructure to controlled chaos experiments. These include:
|
||||
|
||||
- Microservices Infrastructure: Our backend is designed as a microservices architecture, running on Kubernetes. We conduct experiments on inter-service communication, API latencies, and service resilience during node failures or resource constraints.
|
||||
- Load Balancers and Networking: We simulate disruptions in networking, such as packet drops or DNS failures, to ensure our applications maintain connectivity and continue serving users.
|
||||
- Application Workloads: High-demand applications like our gaming engine and payment/promotions api's are put under stress to evaluate their fault tolerance and recovery mechanisms during peak loads or unexpected outages.
|
||||
|
||||
### **Why do we use Litmus.**
|
||||
|
||||
We chose Litmus Chaos for several compelling reasons:
|
||||
|
||||
- Kubernetes-Native Integration: Since our infrastructure is heavily Kubernetes-based, Litmus seamlessly integrates with our stack, making it a natural fit.
|
||||
- Ease of Use and Open-Source: Litmus offers a user-friendly interface along with robust documentation, allowing our teams to adopt it quickly without steep learning curves.
|
||||
- Custom Experiment Support: The ability to create tailored chaos experiments aligned with our specific workloads ensures we can target critical failure scenarios unique to our ecosystem.
|
||||
- Community Support and Scalability: Being an open-source project with an active community, Litmus evolves rapidly, allowing us to leverage the latest chaos engineering methodologies and tools.
|
||||
|
||||
Litmus has been instrumental in identifying hidden weaknesses in our system, such as unexpected dependencies or cascading failures. This has enabled us to proactively address potential issues, enhance system resilience, and meet our uptime commitments.
|
||||
|
||||
### **Where are we using Litmus.**
|
||||
|
||||
We use Litmus Chaos in various environments to ensure robust testing at every stage of development:
|
||||
|
||||
- Development: Initial chaos experiments are conducted in isolated dev environments to identify basic resilience issues and ensure service fault tolerance during early-stage development.
|
||||
- Staging/Pre-Production: In staging, we run more comprehensive chaos scenarios simulating real-world failures, such as pod crashes, resource exhaustion, or external API downtime, to ensure the production-like environment is resilient.
|
||||
- Production: Selected, low-risk chaos experiments are conducted in production under strict supervision to verify real-time system robustness and validate SLAs in live conditions.
|
||||
|
||||
Litmus Chaos has transformed our approach to building and maintaining a highly resilient gaming platform, allowing us to deliver exceptional user experiences while preparing for the unexpected.
|
|
@ -0,0 +1,32 @@
|
|||
## **Pole Emploi**
|
||||
|
||||
[Pole Emploi](https://www.pole-emploi.fr/accueil/) is the public employment service in France.
|
||||
Its roles are, on the one hand, to compensate job seekers and help them find a job, and on the other hand, to guide companies in their recruitment.
|
||||
In order to do that, Pôle emploi's agents are mobilized on a daily basis to anticipate trends, innovate and bring together key players and relays in the field.
|
||||
|
||||
|
||||
### **Why we explored litmus**
|
||||
|
||||
With around 5.6 millions end-users, and applications that generate hudge traffic and datas,
|
||||
the resiliency of our apps and infrastructures is a must-have!
|
||||
|
||||
- Litmus is OPEN SOURCE
|
||||
- Litmus is designed for Kubernetes
|
||||
- Litmus is easy to deploy ( helm chart )
|
||||
- Litmus is extendable ( experiments libs, predefined workflows, private hubs )
|
||||
|
||||
|
||||
### How we explored litmus
|
||||
|
||||
We explored Litmus to validate and increase the resiliency of our private and public Kubernetes clouds.
|
||||
|
||||
- Testing resiliency of Kubernetes Infrastructure Components
|
||||
- Testing resiliency of Public applications hosted on Kubernetes
|
||||
- Testing resiliency of Private applications hosted on Kubernetes
|
||||
|
||||
|
||||
### Benefits of litmus
|
||||
|
||||
Litmus allowed us to identify and work on the observability, configuration, and high availability of certain components which were not resilient.
|
||||
|
||||
With the history of experiences, the Reliability score system, and all the statistics on the chaos tests, using random recurring tests, litmus allows us to audit, over time, the resilience of our platforms, and to have a global vision of the state of it.
|
|
@ -0,0 +1,14 @@
|
|||
### **Pravega**
|
||||
|
||||
[Pravega](https://pravega.io/) is an open source storage system implementing **Streams** as a first-class primitive for storing/serving continuous and unbounded data. Pravega organizes data into **Streams**. A **Stream** is a durable, elastic, append-only, unbounded sequence of bytes having good performance and strong consistency.
|
||||
Pravega Streams are based on an append-only log data structure. By using append-only logs, Pravega rapidly ingests data into durable storage.
|
||||
|
||||
### Why do we use litmus
|
||||
|
||||
**Pravega** is a distributed system and is deployed on our custom build of Kubernetes having the desired set of microservices, hence we were seeking a tool which can adapt in our environment with minimal alteration and be able to inject faults while exercising quality tests on our product.
|
||||
|
||||
Therefore, we chose Litmus Chaos to meet our use cases. The benefits we see in Litmus Chaos are: it is a CNCF project, it supports kubernetes type deployment environment, it has frequent & steady releases, and it's a well maintained tool.
|
||||
|
||||
### How do we use litmus
|
||||
|
||||
On deploying Litmus Chaos along with its **Chaos Experiments**, we get standard fault injection scenarios like: **pod-network-loss**, **pod-network-latency** & **pod-cpu-hog**, which we introduce on live deployments of a Pravega cluster. This helps us to simulate real time stressful conditions on the setup and to test for its recovery & fault-tolerance behavior, which in turn helps us to improve the overall quality of our product in stable as well as adverse conditions.
|
|
@ -0,0 +1,21 @@
|
|||
# Red Hat
|
||||
[Red Hat](https://www.redhat.com) is an enterprise software company with an open source development model. [Openshift](https://www.openshift.com/) is a family of containerization software products developed by Red Hat
|
||||
|
||||
## Why do we use Litmus.
|
||||
We wanted to test the maturity of Red Hat Openshift Container Platform and layers on top of it using chaos testing. Following that, we decided to use Litmus for these reasons:
|
||||
- It's an Open Source project
|
||||
- It has a wide selection of experiments available
|
||||
- It has a vibrant community
|
||||
- There are frequent releases and it is well maintained
|
||||
|
||||
## How do we use Litmus.
|
||||
We consume a variety of Litmus experiments in a tool called [Kraken](https://github.com/cloud-bulldozer/kraken), where we are able to test infrastructure components of OpenShift.
|
||||
Litmus experiments are deployed against a single Openshift cluster that runs on top of a variety of cloud providers and a baremetal server using libvirt/KVM.
|
||||
Each experiment consists of observing the behavior upon applying chaos to the underlying infrastucture of a running pod or node instance, and validating the results of the resiliency.
|
||||
The chaos we inject to the VMs that host the openshift nodes can vary from hogging up the CPU and memory to stressing the IO and network disruption at the node level, among others.
|
||||
|
||||
|
||||
## Benefits in using Litmus.
|
||||
Being a cloud native solution, Litmus allows us to define our experiment and expectations in the `chaosexperiment` manifest and retrieve the results in the `chaosresult` object generated at runtime.
|
||||
Its vast selection of experiments, periodic release cadence and a welcoming community were sufficient signals that ensured with Litmus we would achieve our goal.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Wingie Enuygun Company
|
||||
[Wingie Enuygun Company](https://www.wingie.com/) is a leading travel and technology company providing seamless travel solutions across various platforms.
|
||||
|
||||
## Why do we use Litmus
|
||||
We use Litmus to identify bottlenecks in our systems, detect issues early, and foresee potential errors. This allows us to take proactive measures and maintain the resilience and performance of our infrastructure.
|
||||
|
||||
## How do we use Litmus
|
||||
Litmus is integrated into our QA cycles, where it plays a crucial role in catching bugs and verifying the overall resilience of our systems.
|
||||
|
||||
## Benefits in using Litmus
|
||||
Litmus chaos experiments are straightforward to implement and can be easily customized or extended to meet our specific requirements, enabling us to effectively manage and optimize our systems at Wingie Enuygun.
|
|
@ -0,0 +1,163 @@
|
|||
# Makefile for building Chaos Center
|
||||
# Reference Guide - https://www.gnu.org/software/make/manual/make.html
|
||||
|
||||
#
|
||||
# Internal variables or constants.
|
||||
# NOTE - These will be executed when any make target is invoked.
|
||||
#
|
||||
IS_DOCKER_INSTALLED = $(shell which docker >> /dev/null 2>&1; echo $$?)
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo ""
|
||||
@echo "Usage:-"
|
||||
@echo "\tmake all -- [default] builds the litmus containers"
|
||||
@echo ""
|
||||
|
||||
.PHONY: deps
|
||||
deps: _build_check_docker
|
||||
|
||||
_build_check_docker:
|
||||
@echo "------------------"
|
||||
@echo "--> Check the Docker deps"
|
||||
@echo "------------------"
|
||||
@if [ $(IS_DOCKER_INSTALLED) -eq 1 ]; \
|
||||
then echo "" \
|
||||
&& echo "ERROR:\tdocker is not installed. Please install it before build." \
|
||||
&& echo "" \
|
||||
&& exit 1; \
|
||||
fi;
|
||||
|
||||
.PHONY: chaos-center-check
|
||||
chaos-center-check : frontend-services-checks backend-services-checks
|
||||
|
||||
frontend-services-checks:
|
||||
@echo "------------------"
|
||||
@echo "--> checking code style [frontend]"
|
||||
@echo "------------------"
|
||||
cd web && yarn -s && yarn lint
|
||||
# @echo "------------------"
|
||||
# @echo "--> Check chaos-center frontend [depcheck]"
|
||||
# @echo "------------------"
|
||||
# cd web && npx depcheck --skip-missing .
|
||||
|
||||
backend-services-checks:
|
||||
@echo "------------------"
|
||||
@echo "--> checking code style [backend]"
|
||||
@echo "------------------"
|
||||
@fmtRes=$$(gofmt -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \
|
||||
if [ -n "$${fmtRes}" ]; then \
|
||||
echo "gofmt checking failed!" && echo "$${fmtRes}" \
|
||||
&& echo "Please ensure you are using $$($(GO) version) for formatting code." \
|
||||
&& exit 1;\
|
||||
fi
|
||||
@echo "------------------"
|
||||
@echo "--> Check chaos-center graphql-server [go mod tidy]"
|
||||
@echo "------------------"
|
||||
@tidyRes=$$(cd graphql/server && go mod tidy); \
|
||||
if [ -n "$${tidyRes}" ]; then \
|
||||
echo "go mod tidy checking failed!" && echo "$${tidyRes}" \
|
||||
&& echo "Please ensure you are using $$($(GO) version) for formatting code." \
|
||||
&& exit 1; \
|
||||
fi
|
||||
@echo "------------------"
|
||||
@echo "--> Check chaos-center authentication [go mod tidy]"
|
||||
@echo "------------------"
|
||||
@tidyRes=$$(cd authentication && go mod tidy); \
|
||||
if [ -n "$${tidyRes}" ]; then \
|
||||
echo "go mod tidy checking failed!" && echo "$${tidyRes}" \
|
||||
&& echo "Please ensure you are using $$($(GO) version) for formatting code." \
|
||||
&& exit 1; \
|
||||
fi
|
||||
@echo "------------------"
|
||||
@echo "--> Check chaos-center subscriber [go mod tidy]"
|
||||
@echo "------------------"
|
||||
@tidyRes=$$(cd subscriber && go mod tidy); \
|
||||
if [ -n "$${tidyRes}" ]; then \
|
||||
echo "go mod tidy checking failed!" && echo "$${tidyRes}" \
|
||||
&& echo "Please ensure you are using $$($(GO) version) for formatting code" \
|
||||
&& exit 1; \
|
||||
fi
|
||||
@echo "------------------"
|
||||
@echo "--> Check chaos-center event tracker [go mod tidy]"
|
||||
@echo "------------------"
|
||||
@tidyRes=$$(cd cluster-agents/event-tracker && go mod tidy); \
|
||||
if [ -n "$${tidyRes}" ]; then \
|
||||
echo "go mod tidy checking failed!" && echo "$${tidyRes}" \
|
||||
&& echo "Please ensure you are using $$($(GO) version) for formatting code" \
|
||||
&& exit 1; \
|
||||
fi
|
||||
# @echo "------------------"
|
||||
# @echo "--> Check chaos-center upgrade agent [go mod tidy]"
|
||||
# @echo "------------------"
|
||||
# @tidyRes=$$(cd upgrade-agents/control-plane && go mod tidy); \
|
||||
# if [ -n "$${tidyRes}" ]; then \
|
||||
# echo "go mod tidy checking failed!" && echo "$${tidyRes}" \
|
||||
# && echo "Please ensure you are using $$($(GO) version) for formatting code" \
|
||||
# && exit 1; \
|
||||
# fi
|
||||
|
||||
backend-unit-tests:
|
||||
@echo "------------------"
|
||||
@echo "--> Running backend unit tests"
|
||||
@echo "------------------"
|
||||
@cd graphql/server && go test -cover ./...
|
||||
@cd authentication && go test -v ./...
|
||||
@#cd cluster-agents/subscriber && go test -v ./...
|
||||
# @cd cluster-agents/event-tracker && go test -v ./...
|
||||
|
||||
web-unit-tests:
|
||||
@echo "------------------"
|
||||
@echo "--> Running frontend unit tests"
|
||||
@echo "------------------"
|
||||
@cd web && yarn -s && yarn test --coverage
|
||||
|
||||
.PHONY: docker.buildx
|
||||
docker.buildx:
|
||||
@echo "------------------------------"
|
||||
@echo "--> Setting up Builder "
|
||||
@echo "------------------------------"
|
||||
@if ! docker buildx ls | grep -q multibuilder; then\
|
||||
docker buildx create --name multibuilder;\
|
||||
docker buildx inspect multibuilder --bootstrap;\
|
||||
docker buildx use multibuilder;\
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes;\
|
||||
fi
|
||||
|
||||
buildx.push.image:
|
||||
@cd $(DIRECTORY) && \
|
||||
docker buildx build -f Dockerfile --progress plain --push --no-cache --platform $(PLATFORMS) -t $(REPONAME)/$(IMAGE_NAME):$(IMG_TAG) .
|
||||
|
||||
.PHONY: push-portal-component
|
||||
push-portal-component: docker.buildx buildx.push.image
|
||||
|
||||
.PHONY: push-frontend
|
||||
push-frontend: docker.buildx buildx.push.frontend
|
||||
|
||||
buildx.push.frontend:
|
||||
@cd web && \
|
||||
if [ "${IMG_TAG}" = "ci" ]; then \
|
||||
docker build . -f Dockerfile -t $(REPONAME)/$(FRONTEND_IMAGE):$(IMG_TAG) --build-arg PUBLIC_URL="$(PUBLIC_URL)" --build-arg TARGETARCH=amd64;\
|
||||
docker push $(REPONAME)/$(FRONTEND_IMAGE):$(IMG_TAG);\
|
||||
else \
|
||||
docker buildx build . -f Dockerfile --progress plain --push --no-cache --platform $(PLATFORMS) -t $(REPONAME)/$(FRONTEND_IMAGE):$(IMG_TAG) --build-arg PUBLIC_URL="$(PUBLIC_URL)";\
|
||||
fi
|
||||
|
||||
.PHONY: push-portal-component-amd64
|
||||
push-portal-component-amd64: docker-build-portal-amd64 docker-push-portal-amd64
|
||||
|
||||
docker-build-portal-amd64:
|
||||
@cd $(DIRECTORY) && \
|
||||
docker build . -f Dockerfile -t $(REPONAME)/$(IMAGE_NAME):$(IMG_TAG) --build-arg TARGETARCH=amd64
|
||||
docker-push-portal-amd64:
|
||||
@docker push $(REPONAME)/$(IMAGE_NAME):$(IMG_TAG)
|
||||
|
||||
.PHONY: push-frontend-amd64
|
||||
push-frontend-amd64: docker-build-frontend-amd64 docker-push-frontend-amd64
|
||||
|
||||
docker-build-frontend-amd64:
|
||||
@cd frontend && \
|
||||
docker build . -f Dockerfile -t $(REPONAME)/$(IMAGE_NAME):$(IMG_TAG) --build-arg TARGETARCH=amd64 --build-arg REACT_APP_KB_CHAOS_VERSION=$(IMG_TAG) \
|
||||
--build-arg REACT_APP_BUILD_TIME="$(timestamp)" --build-arg PUBLIC_URL="$(PUBLIC_URL)" --build-arg REACT_APP_HUB_BRANCH_NAME="v1.13.x"
|
||||
docker-push-frontend-amd64:
|
||||
@docker push $(REPONAME)/$(IMAGE_NAME):$(IMG_TAG)
|
|
@ -0,0 +1,123 @@
|
|||
# README for Setting Up API Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This guide details the steps to set up and generate API documentation for your project using Swagger and GoSwagger. Swagger is used to create an OpenAPI specification file (`swagger.yaml`), and GoSwagger serves this specification file on a local server.
|
||||
.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before beginning, ensure that you have the following installed:
|
||||
|
||||
- Go programming language environment
|
||||
- `swaggo/swag` library
|
||||
- `goswagger.io` tool
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1: Install Swagger
|
||||
|
||||
First, you need to install `swag`, a tool for generating Swagger 2.0 documents for Go applications. Use the following command to install it:
|
||||
|
||||
```bash
|
||||
go get -u github.com/swaggo/swag/cmd/swag
|
||||
```
|
||||
|
||||
For more details, visit the [swag GitHub repository](https://github.com/swaggo/swag).
|
||||
|
||||
### Step 2: Install GoSwagger
|
||||
|
||||
Next, install GoSwagger, which will serve your Swagger specification file:
|
||||
|
||||
```bash
|
||||
go get -u github.com/go-swagger/go-swagger/cmd/swagger
|
||||
```
|
||||
|
||||
For additional information, refer to the [GoSwagger website](https://goswagger.io/).
|
||||
|
||||
## Setting Up Documentation
|
||||
|
||||
### Step 1: Annotate Your API
|
||||
|
||||
You need to annotate your Go functions to define the API specifications. These annotations are used by Swagger to generate documentation.
|
||||
|
||||
#### Example Annotation:
|
||||
|
||||
```go
|
||||
// DexLogin godoc
|
||||
//
|
||||
// @Description DexRouter creates all the required routes for OAuth purposes.
|
||||
// @Tags DexRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Response{}
|
||||
// @Router /dex/login [get]
|
||||
//
|
||||
// DexLogin handles and redirects to DexServer to proceed with OAuth
|
||||
func DexLogin() gin.HandlerFunc {
|
||||
// ... function implementation ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Formatting:
|
||||
|
||||
After adding the annotations run this command to fix and update the annotation formatting
|
||||
|
||||
```bash
|
||||
swag fmt .
|
||||
```
|
||||
|
||||
### Step 3: Define Response Structures in `doc.go`
|
||||
|
||||
In your handler folder, create or update a file named `doc.go`. Here, define the structures for your responses and errors.
|
||||
|
||||
#### Example Structures:
|
||||
|
||||
```go
|
||||
package handler
|
||||
|
||||
// LoginResponse represents the response structure for login.
|
||||
type LoginResponse struct {
|
||||
accessToken string
|
||||
projectID string
|
||||
projectRole string
|
||||
expiresIn string
|
||||
}
|
||||
|
||||
// ErrServerError represents an error structure for server errors.
|
||||
type ErrServerError struct {
|
||||
Code int `json:"code" example:"500"`
|
||||
Message string `json:"message" example:"Unexpected server error"`
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Generate Swagger Documentation
|
||||
|
||||
After annotating your API and defining your responses, run the following command in your project root to generate the `swagger.yaml` file:
|
||||
|
||||
```bash
|
||||
swag init --parseDependency true
|
||||
```
|
||||
|
||||
This command scans your project and creates a Swagger specification from your annotations.
|
||||
|
||||
### Step 5: Serve the Swagger Specification
|
||||
|
||||
Finally, use GoSwagger to serve your Swagger specification file. This allows you to view your API documentation in a web browser. By default the API docs will be generated with Redocly.
|
||||
|
||||
```bash
|
||||
swagger serve swagger.yaml
|
||||
```
|
||||
|
||||
To run the orginal swagger format
|
||||
|
||||
```bash
|
||||
swagger serve -F=swagger swagger.yaml
|
||||
```
|
||||
|
||||
This command starts a local server hosting your API documentation.
|
||||
|
||||
## Conclusion
|
||||
|
||||
With these steps, you should now have a fully functional API documentation setup using Swagger and GoSwagger. Remember to regularly update your annotations and regenerate the Swagger file to keep your documentation in sync with your API.
|
|
@ -0,0 +1,33 @@
|
|||
# BUILD STAGE
|
||||
FROM golang:1.22 AS builder
|
||||
|
||||
ARG TARGETOS=linux
|
||||
ARG TARGETARCH
|
||||
|
||||
ADD . /auth-server
|
||||
WORKDIR /auth-server
|
||||
|
||||
ENV GOOS=${TARGETOS} \
|
||||
GOARCH=${TARGETARCH}
|
||||
|
||||
RUN go env
|
||||
|
||||
RUN CGO_ENABLED=0 go build -o /output/server -v ./api/
|
||||
|
||||
# Packaging stage
|
||||
# Use RedHat UBI minimal image as base
|
||||
FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5
|
||||
|
||||
LABEL maintainer="LitmusChaos"
|
||||
|
||||
ENV APP_DIR="/litmus"
|
||||
|
||||
COPY --from=builder /output/server $APP_DIR/
|
||||
RUN chown 65534:0 $APP_DIR/server && chmod 755 $APP_DIR/server
|
||||
|
||||
WORKDIR $APP_DIR
|
||||
USER 65534
|
||||
|
||||
CMD ["./server"]
|
||||
|
||||
EXPOSE 3000
|
|
@ -0,0 +1,189 @@
|
|||
package response
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Response string
|
||||
}
|
||||
|
||||
type ApiTokenResponse struct {
|
||||
UserID string
|
||||
Name string
|
||||
Token string
|
||||
ExpiresAt int64
|
||||
CreatedAt int64
|
||||
}
|
||||
|
||||
type Role string
|
||||
type UserResponse struct {
|
||||
ID string `bson:"_id,omitempty" json:"userID"`
|
||||
Username string `bson:"username,omitempty" json:"username"`
|
||||
Password string `bson:"password,omitempty" json:"password,omitempty"`
|
||||
Email string `bson:"email,omitempty" json:"email,omitempty"`
|
||||
Name string `bson:"name,omitempty" json:"name,omitempty"`
|
||||
Role Role `bson:"role,omitempty" json:"role"`
|
||||
DeactivatedAt *int64 `bson:"deactivated_at,omitempty" json:"deactivatedAt,omitempty"`
|
||||
}
|
||||
|
||||
type CapabilitiesResponse struct {
|
||||
Dex struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
} `json:"dex"`
|
||||
}
|
||||
|
||||
type MessageResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type NewApiToken struct {
|
||||
accessToken string
|
||||
}
|
||||
|
||||
type LoginResponse struct {
|
||||
accessToken string
|
||||
projectID string
|
||||
projectRole string
|
||||
expiresIn string
|
||||
}
|
||||
|
||||
// HTTPError example
|
||||
|
||||
func NewError(ctx *gin.Context, status int, err error) {
|
||||
er := HTTPError{
|
||||
Code: status,
|
||||
Message: err.Error(),
|
||||
}
|
||||
ctx.JSON(status, er)
|
||||
}
|
||||
|
||||
type HTTPError struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"status bad request"`
|
||||
}
|
||||
|
||||
type ErrServerError struct {
|
||||
Code int `json:"code" example:"500"`
|
||||
Message string `json:"message" example:"The authorization server encountered an unexpected condition that prevented it from fulfilling the request"`
|
||||
}
|
||||
type ErrInvalidCredentials struct {
|
||||
Code int `json:"code" example:"401"`
|
||||
Message string `json:"message" example:"Invalid Credentials"`
|
||||
}
|
||||
|
||||
type ErrInvalidRequest struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed"`
|
||||
}
|
||||
|
||||
type ErrOldPassword struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"The old and new passwords can't be same"`
|
||||
}
|
||||
|
||||
type ErrUnauthorized struct {
|
||||
Code int `json:"code" example:"401"`
|
||||
Message string `json:"message" example:"The user does not have requested authorization to access this resource"`
|
||||
}
|
||||
|
||||
type ErrUserExists struct {
|
||||
Code int `json:"code" example:"401"`
|
||||
Message string `json:"message" example:"This username is already assigned to another user"`
|
||||
}
|
||||
|
||||
type ErrUserNotFound struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"user does not exist"`
|
||||
}
|
||||
|
||||
type ErrUserDeactivated struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"your account has been deactivated"`
|
||||
}
|
||||
|
||||
type ErrStrictPasswordPolicyViolation struct {
|
||||
Code int `json:"code" example:"401"`
|
||||
Message string `json:"message" example:"Please ensure the password is atleast 8 characters and atmost 16 characters long and has atleast 1 digit, 1 lowercase alphabet, 1 uppercase alphabet and 1 special character"`
|
||||
}
|
||||
|
||||
type ErrStrictUsernamePolicyViolation struct {
|
||||
Code int `json:"code" example:"401"`
|
||||
Message string `json:"message" example:"The username should be atleast 3 characters long and atmost 16 characters long."`
|
||||
}
|
||||
|
||||
type ErrEmptyProjectName struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"Project name can't be empty"`
|
||||
}
|
||||
|
||||
type ErrInvalidRole struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"Role is invalid"`
|
||||
}
|
||||
|
||||
type ErrProjectNotFound struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"This project does not exist"`
|
||||
}
|
||||
|
||||
type ErrInvalidEmail struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"Email address is invalid"`
|
||||
}
|
||||
|
||||
type ErrProjectNotFoundstruct struct {
|
||||
Code int `json:"code" example:"400"`
|
||||
Message string `json:"message" example:"project does not exist"`
|
||||
}
|
||||
|
||||
type ReadinessAPIStatus struct {
|
||||
DataBase string `json:"database"`
|
||||
Collections string `json:"collections"`
|
||||
}
|
||||
|
||||
type APIStatus struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type UserWithProject struct {
|
||||
Data entities.UserWithProject `json:"data"`
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
Data entities.Project `json:"data"`
|
||||
}
|
||||
|
||||
type Projects struct {
|
||||
Data []*entities.Project `json:"data"`
|
||||
}
|
||||
|
||||
type ListProjectResponse struct {
|
||||
Data entities.ListProjectResponse `json:"data"`
|
||||
}
|
||||
|
||||
type ProjectStats struct {
|
||||
Data []*entities.ProjectStats `json:"data"`
|
||||
}
|
||||
|
||||
type Members struct {
|
||||
Data []*entities.Member `json:"data"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
Data entities.Member `json:"data"`
|
||||
}
|
||||
|
||||
type ListInvitationResponse struct {
|
||||
Data []entities.ListInvitationResponse `json:"data"`
|
||||
}
|
||||
|
||||
type ProjectRole struct {
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type ProjectIDWithMessage struct {
|
||||
Message string `json:"message"`
|
||||
ProjectID string `json:"projectID"`
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter/protos"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/validations"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
)
|
||||
|
||||
func (s *ServerGrpc) ValidateRequest(ctx context.Context,
|
||||
inputRequest *protos.ValidationRequest) (*protos.ValidationResponse, error) {
|
||||
token, err := s.ValidateToken(inputRequest.Jwt)
|
||||
if err != nil {
|
||||
return &protos.ValidationResponse{Error: err.Error(), IsValid: false}, err
|
||||
}
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
|
||||
if claims["uid"] == nil {
|
||||
return &protos.ValidationResponse{Error: "token is invalid", IsValid: false}, err
|
||||
}
|
||||
|
||||
uid := claims["uid"].(string)
|
||||
err = validations.RbacValidator(uid, inputRequest.ProjectId,
|
||||
inputRequest.RequiredRoles, inputRequest.Invitation, s.ApplicationService)
|
||||
if err != nil {
|
||||
return &protos.ValidationResponse{Error: err.Error(), IsValid: false}, err
|
||||
}
|
||||
return &protos.ValidationResponse{Error: "", IsValid: true}, nil
|
||||
}
|
||||
|
||||
func (s *ServerGrpc) GetProjectById(ctx context.Context,
|
||||
inputRequest *protos.GetProjectByIdRequest) (*protos.GetProjectByIdResponse, error) {
|
||||
|
||||
project, err := s.ApplicationService.GetProjectByProjectID(inputRequest.ProjectID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fetching user ids of all the members in the project
|
||||
var uids []string
|
||||
|
||||
for _, member := range project.Members {
|
||||
uids = append(uids, member.UserID)
|
||||
}
|
||||
|
||||
memberMap := make(map[string]entities.User)
|
||||
|
||||
authUsers, err := s.ApplicationService.FindUsersByUID(uids)
|
||||
for _, authUser := range *authUsers {
|
||||
memberMap[authUser.ID] = authUser
|
||||
}
|
||||
|
||||
var projectMembers []*protos.ProjectMembers
|
||||
|
||||
// Adding additional details of project members
|
||||
for _, member := range project.Members {
|
||||
var projectMember protos.ProjectMembers
|
||||
projectMember.Email = memberMap[member.UserID].Email
|
||||
projectMember.Username = memberMap[member.UserID].Username
|
||||
projectMember.Invitation = string(member.Invitation)
|
||||
projectMember.Uid = member.UserID
|
||||
projectMember.JoinedAt = strconv.FormatInt(member.JoinedAt, 10)
|
||||
projectMembers = append(projectMembers, &projectMember)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &protos.GetProjectByIdResponse{
|
||||
Id: project.ID,
|
||||
Name: project.Name,
|
||||
Members: projectMembers,
|
||||
State: "",
|
||||
CreatedAt: strconv.FormatInt(project.CreatedAt, 10),
|
||||
UpdatedAt: strconv.FormatInt(project.UpdatedAt, 10),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ServerGrpc) GetUserById(ctx context.Context,
|
||||
inputRequest *protos.GetUserByIdRequest) (*protos.GetUserByIdResponse, error) {
|
||||
user, err := s.ApplicationService.GetUser(inputRequest.UserID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
var deactivatedAt string
|
||||
if user.DeactivatedAt != nil {
|
||||
deactivatedAt = strconv.FormatInt(*user.DeactivatedAt, 10)
|
||||
}
|
||||
return &protos.GetUserByIdResponse{
|
||||
Id: user.ID,
|
||||
Name: user.Name,
|
||||
Username: user.Username,
|
||||
CreatedAt: strconv.FormatInt(user.CreatedAt, 10),
|
||||
UpdatedAt: strconv.FormatInt(user.UpdatedAt, 10),
|
||||
DeactivatedAt: deactivatedAt,
|
||||
Role: string(user.Role),
|
||||
Email: user.Email,
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package grpc_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/grpc"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/mocks"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter/protos"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestGetProjectById(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
projectID string
|
||||
mockGetProjectResponse *entities.Project
|
||||
mockGetProjectError error
|
||||
mockFindUsersResponse *[]entities.User
|
||||
mockFindUsersError error
|
||||
expectedResponse *protos.GetProjectByIdResponse
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "PositiveTestProjectExists",
|
||||
projectID: "project-id",
|
||||
mockGetProjectResponse: &entities.Project{
|
||||
ID: "project-id",
|
||||
Name: "test-project",
|
||||
Members: []*entities.Member{
|
||||
{
|
||||
UserID: "user-1",
|
||||
Invitation: entities.PendingInvitation,
|
||||
JoinedAt: 1234567890,
|
||||
},
|
||||
},
|
||||
},
|
||||
mockGetProjectError: nil,
|
||||
mockFindUsersResponse: &[]entities.User{
|
||||
{
|
||||
ID: "user-1",
|
||||
Email: "user1@email.com",
|
||||
Username: "user1",
|
||||
},
|
||||
},
|
||||
mockFindUsersError: nil,
|
||||
expectedResponse: &protos.GetProjectByIdResponse{
|
||||
Id: "project-id",
|
||||
Name: "test-project",
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "NegativeTestProjectDoesNotExist",
|
||||
projectID: "non-existing-project-id",
|
||||
mockGetProjectResponse: nil,
|
||||
mockGetProjectError: errors.New("project not found"),
|
||||
mockFindUsersResponse: nil,
|
||||
mockFindUsersError: nil,
|
||||
expectedResponse: nil,
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
s := &grpc.ServerGrpc{
|
||||
ApplicationService: &mocks.MockedApplicationService{},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
mockService := s.ApplicationService.(*mocks.MockedApplicationService)
|
||||
mockService.On("GetProjectByProjectID", tc.projectID).Return(tc.mockGetProjectResponse, tc.mockGetProjectError)
|
||||
if tc.mockFindUsersResponse != nil {
|
||||
mockService.On("FindUsersByUID", mock.Anything).Return(tc.mockFindUsersResponse, tc.mockFindUsersError)
|
||||
}
|
||||
|
||||
req := &protos.GetProjectByIdRequest{
|
||||
ProjectID: tc.projectID,
|
||||
}
|
||||
|
||||
resp, err := s.GetProjectById(ctx, req)
|
||||
|
||||
if tc.expectedError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, tc.expectedResponse.Name, resp.Name)
|
||||
}
|
||||
mockService.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserById(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
userID string
|
||||
mockServiceResponse *entities.User
|
||||
mockServiceError error
|
||||
expectedResponse *protos.GetUserByIdResponse
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "PositiveTestUserExists",
|
||||
userID: "user-id",
|
||||
mockServiceResponse: &entities.User{
|
||||
ID: "user-id",
|
||||
Name: "test-user",
|
||||
Username: "username",
|
||||
DeactivatedAt: nil,
|
||||
Role: "admin",
|
||||
Email: "user@email.com",
|
||||
},
|
||||
mockServiceError: nil,
|
||||
expectedResponse: &protos.GetUserByIdResponse{
|
||||
Id: "user-id",
|
||||
Name: "test-user",
|
||||
Email: "user@email.com",
|
||||
DeactivatedAt: "nil",
|
||||
},
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "NegativeTestUserDoesNotExist",
|
||||
userID: "non-existing-user-id",
|
||||
mockServiceResponse: nil,
|
||||
mockServiceError: errors.New("user not found"),
|
||||
expectedResponse: nil,
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
s := &grpc.ServerGrpc{
|
||||
ApplicationService: &mocks.MockedApplicationService{},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
mockService := s.ApplicationService.(*mocks.MockedApplicationService)
|
||||
mockService.On("GetUser", tc.userID).Return(tc.mockServiceResponse, tc.mockServiceError)
|
||||
|
||||
req := &protos.GetUserByIdRequest{
|
||||
UserID: tc.userID,
|
||||
}
|
||||
|
||||
resp, err := s.GetUserById(ctx, req)
|
||||
|
||||
if tc.expectedError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.Equal(t, tc.expectedResponse.Name, resp.Name)
|
||||
assert.Equal(t, tc.expectedResponse.Email, resp.Email)
|
||||
}
|
||||
|
||||
mockService.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package grpc
|
||||
|
||||
import (
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter/protos"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
)
|
||||
|
||||
type ServerGrpc struct {
|
||||
services.ApplicationService
|
||||
protos.UnimplementedAuthRpcServiceServer
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
)
|
||||
|
||||
// GetCapabilities godoc
|
||||
//
|
||||
// @Summary Get capabilities of Auth Server.
|
||||
// @Description Returns capabilities that can be leveraged by frontend services to toggle certain features.
|
||||
// @Tags CapabilitiesRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.CapabilitiesResponse{}
|
||||
// @Router /capabilities [get]
|
||||
//
|
||||
// GetCapabilities returns the capabilities of the Auth Server.
|
||||
func GetCapabilities() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
capabilities := new(response.CapabilitiesResponse)
|
||||
capabilities.Dex.Enabled = utils.DexEnabled
|
||||
c.JSON(200, capabilities)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCapabilities(t *testing.T) {
|
||||
|
||||
testcases := []struct {
|
||||
Name string
|
||||
DexEnabled bool
|
||||
}{
|
||||
{
|
||||
Name: "Dex Enabled",
|
||||
DexEnabled: true,
|
||||
},
|
||||
{
|
||||
Name: "Dex Disabled",
|
||||
DexEnabled: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testcases {
|
||||
test := test
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
utils.DexEnabled = test.DexEnabled
|
||||
|
||||
rest.GetCapabilities()(ctx)
|
||||
capa := response.CapabilitiesResponse{}
|
||||
err := json.Unmarshal(w.Body.Bytes(), &capa)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, test.DexEnabled, capa.Dex.Enabled)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func oAuthDexConfig() (*oauth2.Config, *oidc.IDTokenVerifier, error) {
|
||||
ctx := oidc.ClientContext(context.Background(), &http.Client{})
|
||||
provider, err := oidc.NewProvider(ctx, utils.DexOIDCIssuer)
|
||||
if err != nil {
|
||||
log.Errorf("OAuth Error: Something went wrong with OIDC provider %s", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
return &oauth2.Config{
|
||||
RedirectURL: utils.DexCallBackURL,
|
||||
ClientID: utils.DexClientID,
|
||||
ClientSecret: utils.DexClientSecret,
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
Endpoint: provider.Endpoint(),
|
||||
}, provider.Verifier(&oidc.Config{ClientID: utils.DexClientID}), nil
|
||||
}
|
||||
|
||||
// DexLogin godoc
|
||||
//
|
||||
// @Description DexRouter creates all the required routes for OAuth purposes. .
|
||||
// @Tags DexRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Response{}
|
||||
// @Router /dex/login [get]
|
||||
//
|
||||
// DexLogin handles and redirects to DexServer to proceed with OAuth
|
||||
func DexLogin() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
dexToken, err := utils.GenerateOAuthJWT()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
config, _, err := oAuthDexConfig()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
url := config.AuthCodeURL(dexToken)
|
||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||
}
|
||||
}
|
||||
|
||||
// DexCallback godoc
|
||||
//
|
||||
// @Description DexRouter creates all the required routes for OAuth purposes. .
|
||||
// @Tags DexRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.Response{}
|
||||
// @Router /dex/callback [get]
|
||||
//
|
||||
// DexCallback is the handler that creates/logs in the user from Dex and provides JWT to frontend via a redirect
|
||||
func DexCallback(userService services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
incomingState := c.Query("state")
|
||||
validated, err := utils.ValidateOAuthJWT(incomingState)
|
||||
if !validated {
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/")
|
||||
}
|
||||
config, verifier, err := oAuthDexConfig()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
token, err := config.Exchange(context.Background(), c.Query("code"))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
rawIDToken, ok := token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
log.Error("OAuth Error: no raw id_token found")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
idToken, err := verifier.Verify(c, rawIDToken)
|
||||
if err != nil {
|
||||
log.Error("OAuth Error: no id_token found")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
var claims struct {
|
||||
Name string
|
||||
Email string `json:"email"`
|
||||
Verified bool `json:"email_verified"`
|
||||
}
|
||||
if err := idToken.Claims(&claims); err != nil {
|
||||
log.Error("OAuth Error: claims not found")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
createdAt := time.Now().UnixMilli()
|
||||
|
||||
var userData = entities.User{
|
||||
Name: claims.Name,
|
||||
Email: claims.Email,
|
||||
Username: claims.Email,
|
||||
Role: entities.RoleUser,
|
||||
Audit: entities.Audit{
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: createdAt,
|
||||
},
|
||||
}
|
||||
|
||||
signedInUser, err := userService.LoginUser(&userData)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
salt, err := userService.GetConfig("salt")
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
jwtToken, err := userService.GetSignedJWT(signedInUser, salt.Value)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
var defaultProject string
|
||||
ownerProjects, err := userService.GetOwnerProjectIDs(c, signedInUser.ID)
|
||||
|
||||
if len(ownerProjects) > 0 {
|
||||
defaultProject = ownerProjects[0].ID
|
||||
} else {
|
||||
// Adding user as project owner in project's member list
|
||||
newMember := &entities.Member{
|
||||
UserID: signedInUser.ID,
|
||||
Role: entities.RoleOwner,
|
||||
Invitation: entities.AcceptedInvitation,
|
||||
Username: signedInUser.Username,
|
||||
Name: signedInUser.Name,
|
||||
Email: signedInUser.Email,
|
||||
JoinedAt: time.Now().UnixMilli(),
|
||||
}
|
||||
var members []*entities.Member
|
||||
members = append(members, newMember)
|
||||
state := "active"
|
||||
newProject := &entities.Project{
|
||||
ID: uuid.Must(uuid.NewRandom()).String(),
|
||||
Name: signedInUser.Username + "-project",
|
||||
Members: members,
|
||||
State: &state,
|
||||
Audit: entities.Audit{
|
||||
IsRemoved: false,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
CreatedBy: entities.UserDetailResponse{
|
||||
Username: signedInUser.Username,
|
||||
UserID: signedInUser.ID,
|
||||
Email: signedInUser.Email,
|
||||
},
|
||||
UpdatedAt: time.Now().UnixMilli(),
|
||||
UpdatedBy: entities.UserDetailResponse{
|
||||
Username: signedInUser.Username,
|
||||
UserID: signedInUser.ID,
|
||||
Email: signedInUser.Email,
|
||||
},
|
||||
},
|
||||
}
|
||||
err := userService.CreateProject(newProject)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defaultProject = newProject.ID
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusPermanentRedirect, "/login?jwtToken="+jwtToken+"&projectID="+defaultProject+"&projectRole="+string(entities.RoleOwner))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDexLogin(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
|
||||
rest.DexLogin()(ctx)
|
||||
|
||||
assert.Equal(t, 500, w.Code)
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func contains(s []string, str string) bool {
|
||||
for _, v := range s {
|
||||
if v == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Status godoc
|
||||
//
|
||||
// @Description Status will request users list and return, if successful, a http code 200.
|
||||
// @Tags MiscRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.APIStatus{}
|
||||
// @Router /status [get]
|
||||
//
|
||||
// Status will request users list and return, if successful, a http code 200
|
||||
func Status(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
_, err := service.GetUsers()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, response.APIStatus{Status: "down"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.APIStatus{Status: "up"})
|
||||
}
|
||||
}
|
||||
|
||||
// Readiness godoc
|
||||
//
|
||||
// @Description Return list of tags.
|
||||
// @Tags MiscRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.ReadinessAPIStatus{}
|
||||
// @Router /readiness [get]
|
||||
//
|
||||
// Readiness will return the status of the database and collections
|
||||
func Readiness(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var (
|
||||
dbFlag = "up"
|
||||
colFlag = "up"
|
||||
)
|
||||
|
||||
dbs, err := service.ListDataBase()
|
||||
if !contains(dbs, "auth") {
|
||||
dbFlag = "down"
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, response.ReadinessAPIStatus{DataBase: "down", Collections: "unknown"})
|
||||
return
|
||||
}
|
||||
|
||||
cols, err := service.ListCollection()
|
||||
if !contains(cols, "project") || !contains(cols, "users") {
|
||||
colFlag = "down"
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, response.ReadinessAPIStatus{DataBase: dbFlag, Collections: "down"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response.ReadinessAPIStatus{DataBase: dbFlag, Collections: colFlag})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/mocks"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStatus(t *testing.T) {
|
||||
|
||||
t.Run("Success with valid data", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
users := []entities.User{}
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
mockService.On("GetUsers").Return(&users, nil)
|
||||
rest.Status(mockService)(ctx)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
|
||||
t.Run("Failed with invalid request", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
users := []entities.User{}
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
mockService.On("GetUsers").Return(&users, errors.New("Failed"))
|
||||
rest.Status(mockService)(ctx)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestReadiness(t *testing.T) {
|
||||
t.Run("Success with valid data", func(t *testing.T) {
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
|
||||
mockService.On("ListDataBase").Return([]string{"auth", "otherDB"}, nil)
|
||||
mockService.On("ListCollection").Return([]string{"project", "users", "otherCollection"}, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
rest.Readiness(mockService)(ctx)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
|
||||
t.Run("Failed with invalid data", func(t *testing.T) {
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
|
||||
mockService.On("ListDataBase").Return([]string{"auth", "otherDB"}, errors.New("Failed"))
|
||||
mockService.On("ListCollection").Return([]string{"project", "users", "otherCollection"}, errors.New("Failed"))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
rest.Readiness(mockService)(ctx)
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
})
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,311 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/mocks"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func TestGetUserWithProject(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
t.Run("Failed to retrieve user with projects", func(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
username := "testUser"
|
||||
w := httptest.NewRecorder()
|
||||
c := GetTestGinContext(w)
|
||||
c.Params = gin.Params{
|
||||
{"username", username},
|
||||
}
|
||||
c.Set("username", username)
|
||||
c.Set("role", string(entities.RoleUser))
|
||||
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
response := &entities.ListProjectResponse{}
|
||||
|
||||
request := &entities.ListProjectRequest{
|
||||
UserID: "testUID",
|
||||
}
|
||||
|
||||
service.On("FindUserByUsername", "testUser").Return(user, errors.New("failed"))
|
||||
service.On("GetProjectsByUserID", request).Return(response, errors.New("failed"))
|
||||
|
||||
rest.GetUserWithProject(service)(c)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
})
|
||||
|
||||
t.Run("Successfully retrieve user with projects", func(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
username := "testUser1"
|
||||
f := httptest.NewRecorder()
|
||||
c := GetTestGinContext(f)
|
||||
c.Params = gin.Params{
|
||||
{"username", username},
|
||||
}
|
||||
c.Set("username", username)
|
||||
c.Set("role", string(entities.RoleUser))
|
||||
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser1",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
|
||||
response := &entities.ListProjectResponse{}
|
||||
|
||||
fieldName := entities.ProjectSortingFieldTime
|
||||
|
||||
request := &entities.ListProjectRequest{
|
||||
UserID: "testUID",
|
||||
Pagination: &entities.Pagination{
|
||||
Page: 0,
|
||||
Limit: 15,
|
||||
},
|
||||
Sort: &entities.SortInput{
|
||||
Field: &fieldName,
|
||||
Ascending: nil,
|
||||
},
|
||||
Filter: &entities.ListProjectInputFilter{
|
||||
CreatedByMe: nil,
|
||||
InvitedByOthers: nil,
|
||||
ProjectName: nil,
|
||||
},
|
||||
}
|
||||
|
||||
service.On("FindUserByUsername", "testUser1").Return(user, nil)
|
||||
service.On("GetProjectsByUserID", request).Return(response, nil)
|
||||
|
||||
rest.GetUserWithProject(service)(c)
|
||||
|
||||
assert.Equal(t, http.StatusOK, f.Code)
|
||||
})
|
||||
|
||||
t.Run("Successfully retrieve user with projects if logged user has admin role", func(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
username := "testUser"
|
||||
w := httptest.NewRecorder()
|
||||
c := GetTestGinContext(w)
|
||||
c.Params = gin.Params{
|
||||
{"username", username},
|
||||
}
|
||||
c.Set("username", "adminusername")
|
||||
c.Set("role", string(entities.RoleAdmin))
|
||||
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
Role: entities.RoleAdmin,
|
||||
}
|
||||
response := &entities.ListProjectResponse{}
|
||||
|
||||
fieldName := entities.ProjectSortingFieldTime
|
||||
|
||||
request := &entities.ListProjectRequest{
|
||||
UserID: "testUID",
|
||||
Pagination: &entities.Pagination{
|
||||
Page: 0,
|
||||
Limit: 15,
|
||||
},
|
||||
Sort: &entities.SortInput{
|
||||
Field: &fieldName,
|
||||
Ascending: nil,
|
||||
},
|
||||
Filter: &entities.ListProjectInputFilter{
|
||||
CreatedByMe: nil,
|
||||
InvitedByOthers: nil,
|
||||
ProjectName: nil,
|
||||
},
|
||||
}
|
||||
|
||||
service.On("FindUserByUsername", "testUser").Return(user, nil)
|
||||
service.On("GetProjectsByUserID", request).Return(response, nil)
|
||||
|
||||
rest.GetUserWithProject(service)(c)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetProjectsByUserID(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
t.Run("Failed with invalid data", func(t *testing.T) {
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
ctx.Set("uid", "testUserID")
|
||||
|
||||
response := &entities.ListProjectResponse{}
|
||||
|
||||
fieldName := entities.ProjectSortingFieldTime
|
||||
|
||||
request := &entities.ListProjectRequest{
|
||||
UserID: "testUserID",
|
||||
Pagination: &entities.Pagination{
|
||||
Page: 0,
|
||||
Limit: 15,
|
||||
},
|
||||
Sort: &entities.SortInput{
|
||||
Field: &fieldName,
|
||||
Ascending: nil,
|
||||
},
|
||||
Filter: &entities.ListProjectInputFilter{
|
||||
CreatedByMe: nil,
|
||||
InvitedByOthers: nil,
|
||||
ProjectName: nil,
|
||||
},
|
||||
}
|
||||
|
||||
service := new(mocks.MockedApplicationService)
|
||||
service.On("GetProjectsByUserID", request).Return(response, errors.New("Failed"))
|
||||
rest.GetProjectsByUserID(service)(ctx)
|
||||
assert.Equal(t, utils.ErrorStatusCodes[utils.ErrServerError], w.Code)
|
||||
})
|
||||
|
||||
t.Run("Successful retrieve of project", func(t *testing.T) {
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
ctx.Set("uid", "testUserID")
|
||||
projects := []*entities.Project{
|
||||
{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
},
|
||||
}
|
||||
|
||||
response := &entities.ListProjectResponse{
|
||||
Projects: projects,
|
||||
}
|
||||
|
||||
fieldName := entities.ProjectSortingFieldTime
|
||||
|
||||
request := &entities.ListProjectRequest{
|
||||
UserID: "testUserID",
|
||||
Pagination: &entities.Pagination{
|
||||
Page: 0,
|
||||
Limit: 15,
|
||||
},
|
||||
Sort: &entities.SortInput{
|
||||
Field: &fieldName,
|
||||
Ascending: nil,
|
||||
},
|
||||
Filter: &entities.ListProjectInputFilter{
|
||||
CreatedByMe: nil,
|
||||
InvitedByOthers: nil,
|
||||
ProjectName: nil,
|
||||
},
|
||||
}
|
||||
|
||||
service := new(mocks.MockedApplicationService)
|
||||
service.On("GetProjectsByUserID", request).Return(response, nil)
|
||||
rest.GetProjectsByUserID(service)(ctx)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGetProject(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
t.Run("unauthorized request to Project", func(t *testing.T) {
|
||||
projectID := "testProjectID"
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
ctx.Set("uid", projectID)
|
||||
ctx.Set("role", string(entities.RoleUser))
|
||||
service := new(mocks.MockedApplicationService)
|
||||
project := &entities.Project{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
}
|
||||
user := &entities.User{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
}
|
||||
|
||||
service.On("GetProjectByProjectID", projectID).Return(project, errors.New("Failed"))
|
||||
service.On("GetUser", projectID).Return(user, errors.New("Failed"))
|
||||
rest.GetProject(service)(ctx)
|
||||
|
||||
assert.Equal(t, utils.ErrorStatusCodes[utils.ErrUnauthorized], w.Code)
|
||||
})
|
||||
|
||||
t.Run("Successful to find Project", func(t *testing.T) {
|
||||
projectID := "testUserID"
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
ctx.Set("uid", projectID)
|
||||
ctx.Set("role", string(entities.RoleAdmin))
|
||||
service := new(mocks.MockedApplicationService)
|
||||
project := &entities.Project{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
}
|
||||
user := &entities.User{
|
||||
ID: "testUserID",
|
||||
Name: "Test User",
|
||||
}
|
||||
projects := []*entities.Project{
|
||||
{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
},
|
||||
}
|
||||
expectedFilter := primitive.D{
|
||||
primitive.E{
|
||||
Key: "_id",
|
||||
Value: "",
|
||||
},
|
||||
primitive.E{
|
||||
Key: "members",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "$elemMatch",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "user_id",
|
||||
Value: "testUserID",
|
||||
},
|
||||
primitive.E{
|
||||
Key: "role",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "$in",
|
||||
Value: []string{"Owner", "Viewer", "Executor"},
|
||||
},
|
||||
},
|
||||
},
|
||||
primitive.E{
|
||||
Key: "invitation",
|
||||
Value: "Accepted",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
service.On("GetProjectByProjectID", "").Return(project, nil)
|
||||
service.On("GetUser", projectID).Return(user, nil)
|
||||
service.On("GetProjects", expectedFilter).Return(projects, nil)
|
||||
rest.GetProject(service)(ctx)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
})
|
||||
|
||||
}
|
|
@ -0,0 +1,838 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/validations"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
const BearerSchema = "Bearer "
|
||||
|
||||
// CreateUser godoc
|
||||
//
|
||||
// @Description Create new user.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 400 {object} response.ErrInvalidEmail
|
||||
// @Failure 401 {object} response.ErrStrictPasswordPolicyViolation
|
||||
// @Failure 401 {object} response.ErrStrictUsernamePolicyViolation
|
||||
// @Failure 401 {object} response.ErrUserExists
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.UserResponse{}
|
||||
// @Router /create_user [post]
|
||||
//
|
||||
// CreateUser creates a new user
|
||||
func CreateUser(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userRole := c.MustGet("role").(string)
|
||||
|
||||
if entities.Role(userRole) != entities.RoleAdmin {
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
var userRequest entities.User
|
||||
err := c.BindJSON(&userRequest)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
if userRequest.Role != entities.RoleUser && userRequest.Role != entities.RoleAdmin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
userRequest.Username = utils.SanitizeString(userRequest.Username)
|
||||
if userRequest.Role == "" || userRequest.Username == "" || userRequest.Password == "" {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
//username validation
|
||||
err = utils.ValidateStrictUsername(userRequest.Username)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrStrictUsernamePolicyViolation], presenter.CreateErrorResponse(utils.ErrStrictUsernamePolicyViolation))
|
||||
return
|
||||
}
|
||||
|
||||
// Assigning UID to user
|
||||
uID := uuid.Must(uuid.NewRandom()).String()
|
||||
userRequest.ID = uID
|
||||
userRequest.IsInitialLogin = true
|
||||
|
||||
// Generating password hash
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(userRequest.Password), utils.PasswordEncryptionCost)
|
||||
if err != nil {
|
||||
log.Error("auth error: Error generating password")
|
||||
}
|
||||
password := string(hashedPassword)
|
||||
userRequest.Password = password
|
||||
|
||||
// Validating email address
|
||||
if userRequest.Email != "" {
|
||||
if !userRequest.IsEmailValid(userRequest.Email) {
|
||||
log.Error("auth error: invalid email")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidEmail], presenter.CreateErrorResponse(utils.ErrInvalidEmail))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
createdAt := time.Now().UnixMilli()
|
||||
userRequest.CreatedAt = createdAt
|
||||
|
||||
userResponse, err := service.CreateUser(&userRequest)
|
||||
if errors.Is(err, utils.ErrUserExists) {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUserExists], presenter.CreateErrorResponse(utils.ErrUserExists))
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, userResponse)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUser godoc
|
||||
//
|
||||
// @Description Update users details.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrStrictPasswordPolicyViolation
|
||||
// @Failure 401 {object} response.ErrStrictUsernamePolicyViolation
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /update/details [post]
|
||||
//
|
||||
// UpdateUser updates the user details
|
||||
func UpdateUser(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var userRequest entities.UserDetails
|
||||
err := c.BindJSON(&userRequest)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
uid := c.MustGet("uid").(string)
|
||||
userRequest.ID = uid
|
||||
initialLogin, err := CheckInitialLogin(service, uid)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
err = service.UpdateUser(&userRequest)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "User details updated successfully"})
|
||||
}
|
||||
}
|
||||
|
||||
// GetUser godoc
|
||||
//
|
||||
// @Description Get user.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrUserNotFound
|
||||
// @Success 200 {object} response.UserResponse{}
|
||||
// @Router /get_user/:uid [get]
|
||||
//
|
||||
// GetUser returns the user that matches the uid passed in parameter
|
||||
func GetUser(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid := c.Param("uid")
|
||||
|
||||
// Validating logged-in user
|
||||
// Must be either requesting info from the logged-in user
|
||||
// or any user if it has the admin role
|
||||
role := c.MustGet("role").(string)
|
||||
if c.MustGet("uid").(string) != uid && role != string(entities.RoleAdmin) {
|
||||
log.Error("auth error: unauthorized")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := service.GetUser(uid)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUserNotFound], presenter.CreateErrorResponse(utils.ErrUserNotFound))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, user)
|
||||
}
|
||||
}
|
||||
|
||||
// FetchUsers godoc
|
||||
//
|
||||
// @Description Fetch users.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.UserResponse{}
|
||||
// @Router /users [get]
|
||||
//
|
||||
// FetchUsers fetches all the users
|
||||
func FetchUsers(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userRole := c.MustGet("role").(string)
|
||||
|
||||
if entities.Role(userRole) != entities.RoleAdmin {
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
users, err := service.GetUsers()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, users)
|
||||
}
|
||||
}
|
||||
|
||||
// InviteUsers godoc
|
||||
//
|
||||
// @Description Invite users.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.UserResponse{}
|
||||
// @Router /invite_users/:project_id [get]
|
||||
//
|
||||
// InviteUsers invites users to the project
|
||||
func InviteUsers(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
projectID := c.Param("project_id")
|
||||
if projectID == "" {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
err := validations.RbacValidator(c.MustGet("uid").(string), projectID,
|
||||
validations.MutationRbacRules["sendInvitation"], string(entities.AcceptedInvitation), service)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
projectMembers, err := service.GetProjectMembers(projectID, "all")
|
||||
|
||||
var userIds []string
|
||||
for _, k := range projectMembers {
|
||||
userIds = append(userIds, k.UserID)
|
||||
}
|
||||
users, err := service.InviteUsers(userIds)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"data": users})
|
||||
}
|
||||
}
|
||||
|
||||
// LoginUser godoc
|
||||
//
|
||||
// @Description User Login.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 400 {object} response.ErrUserNotFound
|
||||
// @Failure 400 {object} response.ErrUserDeactivated
|
||||
// @Failure 401 {object} response.ErrInvalidCredentials
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.LoginResponse{}
|
||||
// @Router /login [post]
|
||||
//
|
||||
// LoginUser returns the token for the user if the credentials are valid
|
||||
func LoginUser(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var userRequest entities.User
|
||||
err := c.BindJSON(&userRequest)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
userRequest.Username = utils.SanitizeString(userRequest.Username)
|
||||
if userRequest.Username == "" || userRequest.Password == "" {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
// Checking if user exists
|
||||
user, err := service.FindUserByUsername(userRequest.Username)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidCredentials], presenter.CreateErrorResponse(utils.ErrInvalidCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
// Checking if user is deactivated
|
||||
if user.DeactivatedAt != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidCredentials], presenter.CreateErrorResponse(utils.ErrInvalidCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
// Validating password
|
||||
err = service.CheckPasswordHash(user.Password, userRequest.Password)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidCredentials], presenter.CreateErrorResponse(utils.ErrInvalidCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
salt, err := service.GetConfig("salt")
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
token, err := service.GetSignedJWT(user, salt.Value)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
expiryTime := time.Duration(utils.JWTExpiryDuration) * 60
|
||||
|
||||
var defaultProject string
|
||||
ownerProjects, err := service.GetOwnerProjectIDs(c, user.ID)
|
||||
|
||||
if len(ownerProjects) > 0 {
|
||||
defaultProject = ownerProjects[0].ID
|
||||
} else if !user.IsInitialLogin {
|
||||
// Adding user as project owner in project's member list
|
||||
newMember := &entities.Member{
|
||||
UserID: user.ID,
|
||||
Role: entities.RoleOwner,
|
||||
Invitation: entities.AcceptedInvitation,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
JoinedAt: time.Now().UnixMilli(),
|
||||
}
|
||||
var members []*entities.Member
|
||||
members = append(members, newMember)
|
||||
state := "active"
|
||||
newProject := &entities.Project{
|
||||
ID: uuid.Must(uuid.NewRandom()).String(),
|
||||
Name: user.Username + "-project",
|
||||
Members: members,
|
||||
State: &state,
|
||||
Audit: entities.Audit{
|
||||
IsRemoved: false,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
CreatedBy: entities.UserDetailResponse{
|
||||
Username: user.Username,
|
||||
UserID: user.ID,
|
||||
Email: user.Email,
|
||||
},
|
||||
UpdatedAt: time.Now().UnixMilli(),
|
||||
UpdatedBy: entities.UserDetailResponse{
|
||||
Username: user.Username,
|
||||
UserID: user.ID,
|
||||
Email: user.Email,
|
||||
},
|
||||
},
|
||||
}
|
||||
err := service.CreateProject(newProject)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defaultProject = newProject.ID
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"accessToken": token,
|
||||
"projectID": defaultProject,
|
||||
"projectRole": entities.RoleOwner,
|
||||
"expiresIn": expiryTime,
|
||||
"type": "Bearer",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// LogoutUser godoc
|
||||
//
|
||||
// @Description Revokes the token passed in the Authorization header.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /logout [post]
|
||||
//
|
||||
// LogoutUser revokes the token passed in the Authorization header
|
||||
func LogoutUser(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
tokenString := authHeader[len(BearerSchema):]
|
||||
// revoke token
|
||||
err := service.RevokeToken(tokenString)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "successfully logged out"})
|
||||
}
|
||||
}
|
||||
|
||||
// UpdatePassword godoc
|
||||
//
|
||||
// @Description Update user password.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrStrictPasswordPolicyViolation
|
||||
// @Failure 400 {object} response.ErrOldPassword
|
||||
// @Failure 401 {object} response.ErrInvalidCredentials
|
||||
// @Success 200 {object} response.ProjectIDWithMessage{}
|
||||
// @Router /update/password [post]
|
||||
//
|
||||
// UpdatePassword updates the user password
|
||||
func UpdatePassword(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var userPasswordRequest entities.UserPassword
|
||||
err := c.BindJSON(&userPasswordRequest)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
username := c.MustGet("username").(string)
|
||||
|
||||
// Fetching userDetails
|
||||
user, err := service.FindUserByUsername(username)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUserNotFound], presenter.CreateErrorResponse(utils.ErrInvalidCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
userPasswordRequest.Username = username
|
||||
if userPasswordRequest.NewPassword != "" {
|
||||
err := utils.ValidateStrictPassword(userPasswordRequest.NewPassword)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrStrictPasswordPolicyViolation], presenter.CreateErrorResponse(utils.ErrStrictPasswordPolicyViolation))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
err = service.UpdatePassword(&userPasswordRequest, true)
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
if strings.Contains(err.Error(), "old and new passwords can't be same") {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrOldPassword], presenter.CreateErrorResponse(utils.ErrOldPassword))
|
||||
} else if strings.Contains(err.Error(), "invalid credentials") {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidCredentials], presenter.CreateErrorResponse(utils.ErrInvalidCredentials))
|
||||
} else {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var defaultProject string
|
||||
ownerProjects, err := service.GetOwnerProjectIDs(c, user.ID)
|
||||
|
||||
if len(ownerProjects) > 0 {
|
||||
defaultProject = ownerProjects[0].ID
|
||||
} else {
|
||||
// Adding user as project owner in project's member list
|
||||
newMember := &entities.Member{
|
||||
UserID: user.ID,
|
||||
Role: entities.RoleOwner,
|
||||
Invitation: entities.AcceptedInvitation,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
Email: user.Email,
|
||||
JoinedAt: time.Now().UnixMilli(),
|
||||
}
|
||||
var members []*entities.Member
|
||||
members = append(members, newMember)
|
||||
state := "active"
|
||||
newProject := &entities.Project{
|
||||
ID: uuid.Must(uuid.NewRandom()).String(),
|
||||
Name: user.Username + "-project",
|
||||
Members: members,
|
||||
State: &state,
|
||||
Audit: entities.Audit{
|
||||
IsRemoved: false,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
CreatedBy: entities.UserDetailResponse{
|
||||
Username: user.Username,
|
||||
UserID: user.ID,
|
||||
Email: user.Email,
|
||||
},
|
||||
UpdatedAt: time.Now().UnixMilli(),
|
||||
UpdatedBy: entities.UserDetailResponse{
|
||||
Username: user.Username,
|
||||
UserID: user.ID,
|
||||
Email: user.Email,
|
||||
},
|
||||
},
|
||||
}
|
||||
err := service.CreateProject(newProject)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defaultProject = newProject.ID
|
||||
}
|
||||
c.JSON(http.StatusOK, response.ProjectIDWithMessage{Message: "password has been updated successfully", ProjectID: defaultProject})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ResetPassword godoc
|
||||
//
|
||||
// @Description Reset user password.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 401 {object} response.ErrStrictPasswordPolicyViolation
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /reset/password [post]
|
||||
//
|
||||
// ResetPassword resets the user password
|
||||
func ResetPassword(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userRole := c.MustGet("role").(string)
|
||||
|
||||
if entities.Role(userRole) != entities.RoleAdmin {
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
var userPasswordRequest entities.UserPassword
|
||||
err := c.BindJSON(&userPasswordRequest)
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
uid := c.MustGet("uid").(string)
|
||||
var adminUser entities.User
|
||||
adminUser.Username = c.MustGet("username").(string)
|
||||
adminUser.ID = uid
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, uid)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
if userPasswordRequest.NewPassword != "" {
|
||||
err := utils.ValidateStrictPassword(userPasswordRequest.NewPassword)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrStrictPasswordPolicyViolation], presenter.CreateErrorResponse(utils.ErrStrictPasswordPolicyViolation))
|
||||
return
|
||||
}
|
||||
}
|
||||
if userPasswordRequest.Username == "" || userPasswordRequest.NewPassword == "" {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
err = service.IsAdministrator(&adminUser)
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
err = service.UpdatePassword(&userPasswordRequest, false)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "password has been reset successfully"})
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUserState godoc
|
||||
//
|
||||
// @Description Updates the user state.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 401 {object} response.ErrUnauthorized
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /update/state [post]
|
||||
//
|
||||
// UpdateUserState updates the user state
|
||||
func UpdateUserState(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
userRole := c.MustGet("role").(string)
|
||||
var adminUser entities.User
|
||||
adminUser.Username = c.MustGet("username").(string)
|
||||
adminUser.ID = c.MustGet("uid").(string)
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, adminUser.ID)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
if entities.Role(userRole) != entities.RoleAdmin {
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
var userRequest entities.UpdateUserState
|
||||
err = c.BindJSON(&userRequest)
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
if userRequest.IsDeactivate == nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
// Checking if loggedIn user is admin
|
||||
err = service.IsAdministrator(&adminUser)
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
// Transaction to update state in user and project collection
|
||||
err = service.UpdateStateTransaction(userRequest)
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[err], presenter.CreateErrorResponse(err))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "user's state updated successfully"})
|
||||
}
|
||||
}
|
||||
|
||||
// CreateApiToken godoc
|
||||
//
|
||||
// @Description Creates a new api token for the user.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 400 {object} response.ErrUserNotFound
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.NewApiToken{}
|
||||
// @Router /create_token [post]
|
||||
//
|
||||
// CreateApiToken creates a new api token for the user
|
||||
func CreateApiToken(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var apiTokenRequest entities.ApiTokenInput
|
||||
err := c.BindJSON(&apiTokenRequest)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
// Validating logged-in user
|
||||
// Requesting info must be from the logged-in user
|
||||
if c.MustGet("uid").(string) != apiTokenRequest.UserID {
|
||||
log.Error("auth error: unauthorized")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, apiTokenRequest.UserID)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
// Checking if user exists
|
||||
user, err := service.GetUser(apiTokenRequest.UserID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUserNotFound], presenter.CreateErrorResponse(utils.ErrUserNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
if token, err := service.CreateApiToken(user, apiTokenRequest); err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"accessToken": token,
|
||||
"type": "Bearer",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetApiTokens godoc
|
||||
//
|
||||
// @Description Returns all the api tokens for the user.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.ApiTokenResponse{}
|
||||
// @Router /token/:uid [post]
|
||||
//
|
||||
// GetApiTokens returns all the api tokens for the user
|
||||
func GetApiTokens(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid := c.Param("uid")
|
||||
|
||||
// Validating logged-in user
|
||||
// Requesting info must be from the logged-in user
|
||||
if c.MustGet("uid").(string) != uid {
|
||||
log.Error("auth error: unauthorized")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
apiTokens, err := service.GetApiTokensByUserID(uid)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"apiTokens": apiTokens,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteApiToken godoc
|
||||
//
|
||||
// @Description Delete api token for the user.
|
||||
// @Tags UserRouter
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Failure 400 {object} response.ErrInvalidRequest
|
||||
// @Failure 500 {object} response.ErrServerError
|
||||
// @Success 200 {object} response.MessageResponse{}
|
||||
// @Router /remove_token [post]
|
||||
//
|
||||
// DeleteApiToken deletes the api token for the user
|
||||
func DeleteApiToken(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var deleteApiTokenRequest entities.DeleteApiTokenInput
|
||||
err := c.BindJSON(&deleteApiTokenRequest)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrInvalidRequest], presenter.CreateErrorResponse(utils.ErrInvalidRequest))
|
||||
return
|
||||
}
|
||||
|
||||
// Validating logged-in user
|
||||
// Requesting info must be from the logged-in user
|
||||
if c.MustGet("uid").(string) != deleteApiTokenRequest.UserID {
|
||||
log.Error("auth error: unauthorized")
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
|
||||
// admin/user shouldn't be able to perform any task if it's default pwd is not changes(initial login is true)
|
||||
initialLogin, err := CheckInitialLogin(service, deleteApiTokenRequest.UserID)
|
||||
if err != nil {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
if initialLogin {
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrPasswordNotUpdated))
|
||||
return
|
||||
}
|
||||
|
||||
token := deleteApiTokenRequest.Token
|
||||
err = service.DeleteApiToken(token)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
if err = service.RevokeToken(token); err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, response.MessageResponse{Message: "api token deleted successfully"})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,840 @@
|
|||
package rest_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/mocks"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// TestMain is the entry point for testing
|
||||
func TestMain(m *testing.M) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
log.SetOutput(io.Discard)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func GetTestGinContext(w *httptest.ResponseRecorder) *gin.Context {
|
||||
ctx, _ := gin.CreateTestContext(w)
|
||||
ctx.Request = &http.Request{
|
||||
Header: make(http.Header),
|
||||
URL: &url.URL{},
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
tests := []struct {
|
||||
name string
|
||||
inputBody *entities.User
|
||||
mockRole string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "successfully",
|
||||
inputBody: &entities.User{
|
||||
Username: "newUser",
|
||||
Password: "ValidPassword@1",
|
||||
Email: "newuser@example.com",
|
||||
Name: "John Doe",
|
||||
Role: entities.RoleUser,
|
||||
},
|
||||
mockRole: "admin",
|
||||
given: func() {
|
||||
service.On("CreateUser", mock.AnythingOfType("*entities.User")).Return(&entities.User{
|
||||
ID: "newUserId",
|
||||
Username: "newUser",
|
||||
Email: "newuser@example.com",
|
||||
Name: "John Doe",
|
||||
Role: entities.RoleUser,
|
||||
}, nil).Once()
|
||||
},
|
||||
expectedCode: 200,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Set("role", tc.mockRole)
|
||||
if tc.inputBody != nil {
|
||||
b, err := json.Marshal(tc.inputBody)
|
||||
if err != nil {
|
||||
t.Fatalf("could not marshal input body: %v", err)
|
||||
}
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/users", bytes.NewBuffer(b))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
tc.given()
|
||||
|
||||
rest.CreateUser(service)(c)
|
||||
assert.Equal(t, tc.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUser(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
uid string
|
||||
inputBody *entities.UserDetails
|
||||
given func()
|
||||
expectedCode int
|
||||
expectedMsg string
|
||||
}{
|
||||
{
|
||||
name: "Successful update details",
|
||||
uid: "testUID",
|
||||
inputBody: &entities.UserDetails{Email: "test@email.com", Name: "Test"},
|
||||
given: func() {
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
IsInitialLogin: false,
|
||||
}
|
||||
service.On("GetUser", "testUID").Return(user, nil)
|
||||
service.On("UpdateUser", mock.AnythingOfType("*entities.UserDetails")).Return(nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
expectedMsg: "User details updated successfully",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Set("uid", tt.uid)
|
||||
|
||||
if tt.inputBody != nil {
|
||||
b, _ := json.Marshal(tt.inputBody)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/path", bytes.NewBuffer(b))
|
||||
}
|
||||
|
||||
tt.given()
|
||||
|
||||
rest.UpdateUser(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
var response map[string]string
|
||||
json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.Equal(t, tt.expectedMsg, response["message"])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUser(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
uid string
|
||||
role string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Successfully retrieve user",
|
||||
uid: "testUID",
|
||||
role: "user",
|
||||
given: func() {
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
service.On("GetUser", "testUID").Return(user, nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Params = gin.Params{
|
||||
{"uid", tt.uid},
|
||||
}
|
||||
c.Set("uid", tt.uid)
|
||||
c.Set("role", tt.role)
|
||||
tt.given()
|
||||
|
||||
rest.GetUser(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
if w.Code == http.StatusOK {
|
||||
var user entities.User
|
||||
_ = json.Unmarshal(w.Body.Bytes(), &user)
|
||||
assert.Equal(t, tt.uid, user.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchUsers(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
role string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Successfully retrieve users by admin",
|
||||
role: "admin",
|
||||
given: func() {
|
||||
users := &[]entities.User{
|
||||
{
|
||||
ID: "testUID1",
|
||||
Username: "testUser1",
|
||||
Email: "test1@example.com",
|
||||
},
|
||||
{
|
||||
ID: "testUID2",
|
||||
Username: "testUser2",
|
||||
Email: "test2@example.com",
|
||||
},
|
||||
}
|
||||
service.On("GetUsers").Return(users, nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "Non-admin tries to retrieve users",
|
||||
role: "user",
|
||||
given: func() {},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Set("role", tt.role)
|
||||
|
||||
tt.given()
|
||||
|
||||
rest.FetchUsers(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInviteUsers(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
projectID string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Successfully invite users",
|
||||
projectID: "testProjectID",
|
||||
given: func() {
|
||||
projectMembers := []*entities.Member{
|
||||
{UserID: "user1ID"},
|
||||
{UserID: "user2ID"},
|
||||
}
|
||||
|
||||
service.On("GetProjectMembers", "testProjectID", "all").Return(projectMembers, nil)
|
||||
uids := []string{"user1ID", "user2ID"}
|
||||
users := &[]entities.User{
|
||||
{ID: "user1ID", Username: "user1"},
|
||||
{ID: "user2ID", Username: "user2"},
|
||||
}
|
||||
service.On("InviteUsers", uids).Return(users, nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Set("uid", tt.projectID)
|
||||
c.Params = gin.Params{
|
||||
{"project_id", tt.projectID},
|
||||
}
|
||||
user := &entities.User{
|
||||
ID: "testUserID",
|
||||
Name: "Test User",
|
||||
}
|
||||
project := &entities.Project{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
}
|
||||
projects := []*entities.Project{
|
||||
{
|
||||
ID: "testProjectID",
|
||||
Name: "Test Project",
|
||||
},
|
||||
}
|
||||
expectedFilter := primitive.D{
|
||||
primitive.E{
|
||||
Key: "_id",
|
||||
Value: tt.projectID,
|
||||
},
|
||||
primitive.E{
|
||||
Key: "members",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "$elemMatch",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "user_id",
|
||||
Value: tt.projectID,
|
||||
},
|
||||
primitive.E{
|
||||
Key: "role",
|
||||
Value: primitive.D{
|
||||
primitive.E{
|
||||
Key: "$in",
|
||||
Value: []string{"Owner"},
|
||||
},
|
||||
},
|
||||
},
|
||||
primitive.E{
|
||||
Key: "invitation",
|
||||
Value: "Accepted",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
tt.given()
|
||||
|
||||
service.On("GetProjectByProjectID", "").Return(project, nil)
|
||||
service.On("GetUser", tt.projectID).Return(user, nil)
|
||||
service.On("GetProjects", expectedFilter).Return(projects, nil)
|
||||
rest.InviteUsers(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogoutUser(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
givenToken string
|
||||
given func()
|
||||
expectedCode int
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
name: "Successfully logout",
|
||||
givenToken: "Bearer testToken",
|
||||
given: func() {
|
||||
service.On("RevokeToken", "testToken").Return(nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
expectedOutput: `{"message":"successfully logged out"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
|
||||
c.Request.Header.Set("Authorization", tt.givenToken)
|
||||
|
||||
tt.given()
|
||||
|
||||
rest.LogoutUser(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
assert.Equal(t, tt.expectedOutput, w.Body.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginUser(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input entities.User
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Successfully login user",
|
||||
input: entities.User{
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
},
|
||||
given: func() {
|
||||
userFromDB := &entities.User{
|
||||
ID: "testUserID",
|
||||
Username: "testUser",
|
||||
Password: "hashedPassword",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
service.On("GetConfig", "salt").Return(&authConfig.AuthConfig{}, nil)
|
||||
service.On("FindUserByUsername", "testUser").Return(userFromDB, nil)
|
||||
service.On("CheckPasswordHash", "hashedPassword", "testPassword").Return(nil)
|
||||
service.On("UpdateUserByQuery", mock.Anything, mock.Anything).Return(nil)
|
||||
service.On("GetSignedJWT", userFromDB, mock.Anything).Return("someJWTToken", nil)
|
||||
project := &entities.Project{
|
||||
ID: "someProjectID",
|
||||
}
|
||||
service.On("GetOwnerProjectIDs", mock.Anything, "testUserID").Return([]*entities.Project{project}, nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "Invalid JSON body",
|
||||
given: func() {},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrInvalidRequest],
|
||||
},
|
||||
{
|
||||
name: "Missing Username or Password",
|
||||
given: func() {},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrInvalidRequest],
|
||||
},
|
||||
{
|
||||
name: "User not found",
|
||||
given: func() {
|
||||
service.On("FindUserByUsername", "notFoundUser").Return(nil, errors.New("user not found"))
|
||||
},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrUserNotFound],
|
||||
},
|
||||
{
|
||||
name: "User deactivated",
|
||||
given: func() {
|
||||
deactivatedUser := &entities.User{
|
||||
ID: "deactivatedUserID",
|
||||
Username: "deactivatedUser",
|
||||
Password: "hashedPassword",
|
||||
}
|
||||
service.On("FindUserByUsername", "deactivatedUser").Return(deactivatedUser, nil)
|
||||
},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrUserDeactivated],
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
body, _ := json.Marshal(tt.input)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
tt.given()
|
||||
|
||||
rest.LoginUser(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePassword(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
givenBody string
|
||||
givenUsername string
|
||||
givenStrictPassword bool
|
||||
givenServiceResponse error
|
||||
expectedCode int
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
name: "Successfully update password",
|
||||
givenBody: `{"oldPassword":"oldPass@123", "newPassword":"newPass@123"}`,
|
||||
givenUsername: "testUser",
|
||||
givenStrictPassword: false,
|
||||
givenServiceResponse: nil,
|
||||
expectedCode: http.StatusOK,
|
||||
expectedOutput: `{"message":"password has been updated successfully","projectID":"someProjectID"}`,
|
||||
},
|
||||
{
|
||||
name: "Invalid new password",
|
||||
givenBody: `{"oldPassword":"oldPass@123", "newPassword":"short"}`,
|
||||
givenUsername: "testUser",
|
||||
givenStrictPassword: false,
|
||||
givenServiceResponse: errors.New("invalid password"),
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrStrictPasswordPolicyViolation],
|
||||
expectedOutput: `{"error":"password_policy_violation","errorDescription":"Please ensure the password is atleast 8 characters long and atmost 16 characters long and has atleast 1 digit, 1 lowercase alphabet, 1 uppercase alphabet and 1 special character"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request, _ = http.NewRequest(http.MethodPost, "/", strings.NewReader(tt.givenBody))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Set("username", tt.givenUsername)
|
||||
|
||||
userPassword := entities.UserPassword{
|
||||
Username: tt.givenUsername,
|
||||
OldPassword: "oldPass@123",
|
||||
NewPassword: "newPass@123",
|
||||
}
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
IsInitialLogin: false,
|
||||
}
|
||||
userFromDB := &entities.User{
|
||||
ID: "testUserID",
|
||||
Username: "testUser",
|
||||
Password: "hashedPassword",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
service.On("FindUserByUsername", "testUser").Return(userFromDB, nil)
|
||||
service.On("GetUser", "testUID").Return(user, nil)
|
||||
service.On("UpdatePassword", &userPassword, true).Return(tt.givenServiceResponse)
|
||||
project := &entities.Project{
|
||||
ID: "someProjectID",
|
||||
}
|
||||
service.On("GetOwnerProjectIDs", mock.Anything, "testUserID").Return([]*entities.Project{project}, nil)
|
||||
rest.UpdatePassword(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
assert.Equal(t, tt.expectedOutput, w.Body.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetPassword(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputBody *entities.UserPassword
|
||||
mockRole string
|
||||
mockUID string
|
||||
mockUsername string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Admin role",
|
||||
inputBody: &entities.UserPassword{
|
||||
Username: "testUser",
|
||||
OldPassword: "",
|
||||
NewPassword: "ValidPass@123",
|
||||
},
|
||||
mockRole: "admin",
|
||||
mockUID: "testUID",
|
||||
mockUsername: "adminUser",
|
||||
given: func() {
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
IsInitialLogin: false,
|
||||
}
|
||||
service.On("GetUser", "testUID").Return(user, nil)
|
||||
service.On("IsAdministrator", mock.AnythingOfType("*entities.User")).Return(nil)
|
||||
service.On("UpdatePassword", mock.AnythingOfType("*entities.UserPassword"), false).Return(nil)
|
||||
},
|
||||
expectedCode: 200,
|
||||
},
|
||||
{
|
||||
name: "Non-admin role",
|
||||
inputBody: &entities.UserPassword{
|
||||
Username: "testUser",
|
||||
OldPassword: "",
|
||||
NewPassword: "validPass@123",
|
||||
},
|
||||
mockRole: "user",
|
||||
mockUID: "testUID",
|
||||
mockUsername: "user",
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
},
|
||||
{
|
||||
name: "Invalid Request Body",
|
||||
mockRole: "admin",
|
||||
mockUID: "testUID",
|
||||
mockUsername: "adminUser",
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrInvalidRequest],
|
||||
},
|
||||
{
|
||||
name: "Empty Username or Password",
|
||||
inputBody: &entities.UserPassword{},
|
||||
mockRole: "admin",
|
||||
mockUID: "testUID",
|
||||
mockUsername: "adminUser",
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrInvalidRequest],
|
||||
},
|
||||
{
|
||||
name: "Admin role wrong password",
|
||||
inputBody: &entities.UserPassword{
|
||||
Username: "testUser",
|
||||
OldPassword: "",
|
||||
NewPassword: "short",
|
||||
},
|
||||
mockRole: "admin",
|
||||
mockUID: "testUID",
|
||||
mockUsername: "adminUser",
|
||||
given: func() {
|
||||
user := &entities.User{
|
||||
ID: "testUID",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
IsInitialLogin: false,
|
||||
}
|
||||
service.On("GetUser", "testUID").Return(user, nil)
|
||||
service.On("IsAdministrator", mock.AnythingOfType("*entities.User")).Return(nil)
|
||||
service.On("UpdatePassword", mock.AnythingOfType("*entities.UserPassword"), false).Return(nil)
|
||||
},
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrStrictPasswordPolicyViolation],
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.given != nil {
|
||||
tt.given()
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
c := GetTestGinContext(w)
|
||||
c.Request.Method = http.MethodPost
|
||||
bodyBytes, _ := json.Marshal(tt.inputBody)
|
||||
c.Request.Body = io.NopCloser(bytes.NewReader([]byte(bodyBytes)))
|
||||
c.Set("role", tt.mockRole)
|
||||
c.Set("uid", tt.mockUID)
|
||||
c.Set("username", tt.mockUsername)
|
||||
|
||||
rest.ResetPassword(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUserState(t *testing.T) {
|
||||
service := new(mocks.MockedApplicationService)
|
||||
const (
|
||||
myTrue = 0 == 0
|
||||
myFalse = 0 != 0
|
||||
)
|
||||
deactivate := myFalse
|
||||
tests := []struct {
|
||||
name string
|
||||
inputBody *entities.UpdateUserState
|
||||
mockRole string
|
||||
mockUsername string
|
||||
mockUID string
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "successfully",
|
||||
inputBody: &entities.UpdateUserState{
|
||||
Username: "adminUser",
|
||||
IsDeactivate: &deactivate,
|
||||
},
|
||||
mockRole: "admin",
|
||||
mockUsername: "adminUser",
|
||||
mockUID: "tetstUUIS",
|
||||
given: func() {
|
||||
user := &entities.User{
|
||||
ID: "tetstUUIS",
|
||||
Username: "testUser",
|
||||
Email: "test@example.com",
|
||||
IsInitialLogin: false,
|
||||
}
|
||||
service.On("GetUser", "tetstUUIS").Return(user, nil)
|
||||
service.On("IsAdministrator", mock.AnythingOfType("*entities.User")).Return(nil)
|
||||
service.On("UpdateStateTransaction", mock.AnythingOfType("entities.UpdateUserState")).Return(nil)
|
||||
|
||||
},
|
||||
expectedCode: 200,
|
||||
},
|
||||
{
|
||||
name: "failed to desactivate",
|
||||
inputBody: &entities.UpdateUserState{
|
||||
Username: "adminUser",
|
||||
IsDeactivate: nil,
|
||||
},
|
||||
mockRole: "admin",
|
||||
mockUsername: "adminUser",
|
||||
mockUID: "tetstUUIS",
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrInvalidRequest],
|
||||
},
|
||||
{
|
||||
name: "failed to authorize",
|
||||
inputBody: &entities.UpdateUserState{
|
||||
IsDeactivate: &deactivate,
|
||||
},
|
||||
mockRole: "user",
|
||||
mockUsername: "adminUser",
|
||||
mockUID: "tetstUUIS",
|
||||
expectedCode: utils.ErrorStatusCodes[utils.ErrUnauthorized],
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.given != nil {
|
||||
tc.given()
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
c := GetTestGinContext(w)
|
||||
c.Request.Method = http.MethodPost
|
||||
bodyBytes, _ := json.Marshal(tc.inputBody)
|
||||
c.Request.Body = io.NopCloser(bytes.NewReader([]byte(bodyBytes)))
|
||||
c.Set("role", tc.mockRole)
|
||||
c.Set("uid", tc.mockUID)
|
||||
c.Set("username", tc.mockUsername)
|
||||
|
||||
rest.UpdateUserState(service)(c)
|
||||
|
||||
assert.Equal(t, tc.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateApiToken(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputBody *entities.ApiTokenInput
|
||||
given func()
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
name: "Valid Request",
|
||||
inputBody: &entities.ApiTokenInput{
|
||||
UserID: "testUserID",
|
||||
},
|
||||
given: func() {
|
||||
user := &entities.User{ID: "testUserID"}
|
||||
service.On("GetUser", "testUserID").Return(user, nil)
|
||||
service.On("CreateApiToken", user, mock.MatchedBy(func(input entities.ApiTokenInput) bool {
|
||||
return input.UserID == "testUserID"
|
||||
})).Return("sampleToken", nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c := GetTestGinContext(w)
|
||||
bodyBytes, _ := json.Marshal(tt.inputBody)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/api/token", bytes.NewReader(bodyBytes))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Set("uid", tt.inputBody.UserID)
|
||||
tt.given()
|
||||
|
||||
rest.CreateApiToken(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetApiTokens(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
service := new(mocks.MockedApplicationService)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
uid string
|
||||
given func()
|
||||
expectedCode int
|
||||
expectedToken []entities.ApiToken
|
||||
}{
|
||||
{
|
||||
name: "Valid Request",
|
||||
uid: "testUserID",
|
||||
given: func() {
|
||||
returnedTokens := []entities.ApiToken{
|
||||
{Token: "sampleToken1"},
|
||||
{Token: "sampleToken2"},
|
||||
}
|
||||
service.On("GetApiTokensByUserID", "testUserID").Return(returnedTokens, nil)
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
expectedToken: []entities.ApiToken{
|
||||
{Token: "sampleToken1"},
|
||||
{Token: "sampleToken2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Params = []gin.Param{{Key: "uid", Value: tt.uid}}
|
||||
c.Set("uid", tt.uid)
|
||||
tt.given()
|
||||
|
||||
rest.GetApiTokens(service)(c)
|
||||
|
||||
assert.Equal(t, tt.expectedCode, w.Code)
|
||||
if tt.expectedCode == http.StatusOK {
|
||||
var response map[string][]entities.ApiToken
|
||||
json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.Equal(t, tt.expectedToken, response["apiTokens"])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteApiToken(t *testing.T) {
|
||||
// given
|
||||
w := httptest.NewRecorder()
|
||||
ctx := GetTestGinContext(w)
|
||||
service := new(services.ApplicationService)
|
||||
// when
|
||||
rest.DeleteApiToken(*service)(ctx)
|
||||
// then
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
)
|
||||
|
||||
func CheckInitialLogin(applicationService services.ApplicationService, userID string) (bool, error) {
|
||||
user, err := applicationService.GetUser(userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if user.IsInitialLogin {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package response
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
func AddSalt(service services.ApplicationService) error {
|
||||
// generate salt and add/update to user collection
|
||||
// pass the salt in the below func which will act as jwt secret
|
||||
getSalt, err := service.GetConfig("salt")
|
||||
if err != nil && err != mongo.ErrNoDocuments {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
if getSalt != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
salt, err := utils.RandomString(6)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
encodedSalt := base64.StdEncoding.EncodeToString([]byte(salt))
|
||||
|
||||
config := authConfig.AuthConfig{
|
||||
Key: "salt",
|
||||
Value: encodedSalt,
|
||||
}
|
||||
|
||||
err = service.CreateConfig(config)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
response "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
grpcHandler "github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/grpc"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/middleware"
|
||||
grpcPresenter "github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter/protos"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/routes"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/misc"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/project"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/session"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/user"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
AdminUsername string `required:"true" split_words:"true"`
|
||||
AdminPassword string `required:"true" split_words:"true"`
|
||||
DbServer string `required:"true" split_words:"true"`
|
||||
DbUser string `required:"true" split_words:"true"`
|
||||
DbPassword string `required:"true" split_words:"true"`
|
||||
AllowedOrigins []string `split_words:"true" default:"^(http://|https://|)litmuschaos.io(:[0-9]+|)?,^(http://|https://|)localhost(:[0-9]+|)"`
|
||||
}
|
||||
|
||||
var config Config
|
||||
|
||||
func init() {
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
log.SetReportCaller(true)
|
||||
printVersion()
|
||||
err := envconfig.Process("", &config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// @title Chaoscenter API documentation
|
||||
func main() {
|
||||
// send logs to stderr, so we can use 'kubectl logs'
|
||||
_ = flag.Set("logtostderr", "true")
|
||||
_ = flag.Set("v", "3")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
client, err := utils.MongoConnection()
|
||||
if err != nil {
|
||||
log.Fatal("database connection error $s", err)
|
||||
}
|
||||
|
||||
db := client.Database(utils.DBName)
|
||||
|
||||
// Creating User Collection
|
||||
err = utils.CreateCollection(utils.UserCollection, db)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create collection %s", err)
|
||||
}
|
||||
|
||||
err = utils.CreateIndex(utils.UserCollection, utils.UsernameField, db)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create index %s", err)
|
||||
}
|
||||
|
||||
// Creating Project Collection
|
||||
err = utils.CreateCollection(utils.ProjectCollection, db)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create collection %s", err)
|
||||
}
|
||||
|
||||
// Creating AuthConfig Collection
|
||||
err = utils.CreateCollection(utils.AuthConfigCollection, db)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create collection %s", err)
|
||||
}
|
||||
|
||||
// Creating RevokedToken Collection
|
||||
if err = utils.CreateCollection(utils.RevokedTokenCollection, db); err != nil {
|
||||
log.Errorf("failed to create collection %s", err)
|
||||
}
|
||||
|
||||
if err = utils.CreateTTLIndex(utils.RevokedTokenCollection, db); err != nil {
|
||||
log.Errorf("failed to create index %s", err)
|
||||
}
|
||||
|
||||
// Creating ApiToken Collection
|
||||
if err = utils.CreateCollection(utils.ApiTokenCollection, db); err != nil {
|
||||
log.Errorf("failed to create collection %s", err)
|
||||
}
|
||||
|
||||
userCollection := db.Collection(utils.UserCollection)
|
||||
userRepo := user.NewRepo(userCollection)
|
||||
|
||||
projectCollection := db.Collection(utils.ProjectCollection)
|
||||
projectRepo := project.NewRepo(projectCollection)
|
||||
|
||||
revokedTokenCollection := db.Collection(utils.RevokedTokenCollection)
|
||||
revokedTokenRepo := session.NewRevokedTokenRepo(revokedTokenCollection)
|
||||
|
||||
apiTokenCollection := db.Collection(utils.ApiTokenCollection)
|
||||
apiTokenRepo := session.NewApiTokenRepo(apiTokenCollection)
|
||||
|
||||
authConfigCollection := db.Collection(utils.AuthConfigCollection)
|
||||
authConfigRepo := authConfig.NewAuthConfigRepo(authConfigCollection)
|
||||
|
||||
miscRepo := misc.NewRepo(db, client)
|
||||
|
||||
applicationService := services.NewService(userRepo, projectRepo, miscRepo, revokedTokenRepo, apiTokenRepo, authConfigRepo, db)
|
||||
|
||||
err = response.AddSalt(applicationService)
|
||||
if err != nil {
|
||||
log.Fatal("couldn't create salt $s", err)
|
||||
}
|
||||
|
||||
validatedAdminSetup(applicationService)
|
||||
|
||||
if utils.EnableInternalTls {
|
||||
if utils.TlsCertPath != "" && utils.TlSKeyPath != "" {
|
||||
go runGrpcServerWithTLS(applicationService)
|
||||
} else {
|
||||
log.Fatalf("Failure to start chaoscenter authentication GRPC server due to empty TLS cert file path and TLS key path")
|
||||
}
|
||||
} else {
|
||||
go runGrpcServer(applicationService)
|
||||
}
|
||||
|
||||
runRestServer(applicationService)
|
||||
}
|
||||
|
||||
func validatedAdminSetup(service services.ApplicationService) {
|
||||
// Assigning UID to admin
|
||||
uID := uuid.Must(uuid.NewRandom()).String()
|
||||
|
||||
// Generating password hash
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(utils.AdminPassword), utils.PasswordEncryptionCost)
|
||||
if err != nil {
|
||||
log.Println("Error generating password for admin")
|
||||
}
|
||||
password := string(hashedPassword)
|
||||
|
||||
adminUser := entities.User{
|
||||
ID: uID,
|
||||
Username: utils.AdminName,
|
||||
Password: password,
|
||||
Role: entities.RoleAdmin,
|
||||
IsInitialLogin: true,
|
||||
Audit: entities.Audit{
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
UpdatedAt: time.Now().UnixMilli(),
|
||||
},
|
||||
}
|
||||
|
||||
_, err = service.CreateUser(&adminUser)
|
||||
if err != nil && err == utils.ErrUserExists {
|
||||
log.Println("Admin already exists in the database, not creating a new admin")
|
||||
} else if err != nil {
|
||||
log.Fatalf("Unable to create admin, error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func printVersion() {
|
||||
log.Info(fmt.Sprintf("Go Version: %s", runtime.Version()))
|
||||
log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH))
|
||||
}
|
||||
|
||||
func runRestServer(applicationService services.ApplicationService) {
|
||||
// Starting REST server using Gin
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
gin.EnableJsonDecoderDisallowUnknownFields()
|
||||
app := gin.Default()
|
||||
app.Use(middleware.ValidateCors(config.AllowedOrigins))
|
||||
// Enable dex routes only if passed via environment variables
|
||||
if utils.DexEnabled {
|
||||
routes.DexRouter(app, applicationService)
|
||||
}
|
||||
routes.CapabilitiesRouter(app)
|
||||
routes.MiscRouter(app, applicationService)
|
||||
routes.UserRouter(app, applicationService)
|
||||
routes.ProjectRouter(app, applicationService)
|
||||
|
||||
if utils.EnableInternalTls {
|
||||
if utils.TlsCertPath != "" && utils.TlSKeyPath != "" {
|
||||
conf := utils.GetTlsConfig()
|
||||
server := http.Server{
|
||||
Addr: ":" + utils.RestPort,
|
||||
Handler: app,
|
||||
TLSConfig: conf,
|
||||
}
|
||||
log.Infof("Listening and serving HTTPS on :%s", utils.RestPort)
|
||||
err := server.ListenAndServeTLS("", "")
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start litmus-portal authentication REST server due to %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Fatalf("Failure to start chaoscenter authentication REST server due to empty TLS cert file path and TLS key path")
|
||||
}
|
||||
} else {
|
||||
log.Infof("Listening and serving HTTP on :%s", utils.RestPort)
|
||||
err := app.Run(":" + utils.RestPort)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start litmus-portal authentication REST server due to %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runGrpcServer(applicationService services.ApplicationService) {
|
||||
// Starting gRPC server
|
||||
lis, err := net.Listen("tcp", ":"+utils.GrpcPort)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start litmus-portal authentication server due"+
|
||||
" to %s", err)
|
||||
}
|
||||
grpcApplicationServer := grpcHandler.ServerGrpc{ApplicationService: applicationService}
|
||||
grpcServer := grpc.NewServer()
|
||||
grpcPresenter.RegisterAuthRpcServiceServer(grpcServer, &grpcApplicationServer)
|
||||
log.Infof("Listening and serving gRPC on :%s", utils.GrpcPort)
|
||||
err = grpcServer.Serve(lis)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start chaoscenter authentication GRPC server due to %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func runGrpcServerWithTLS(applicationService services.ApplicationService) {
|
||||
|
||||
// Starting gRPC server
|
||||
lis, err := net.Listen("tcp", ":"+utils.GrpcPort)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start litmus-portal authentication server due to %s", err)
|
||||
}
|
||||
|
||||
// configuring TLS config based on provided certificates & keys
|
||||
conf := utils.GetTlsConfig()
|
||||
|
||||
// create tls credentials
|
||||
tlsCredentials := credentials.NewTLS(conf)
|
||||
|
||||
// create grpc server with tls credential
|
||||
grpcServer := grpc.NewServer(grpc.Creds(tlsCredentials))
|
||||
|
||||
grpcApplicationServer := grpcHandler.ServerGrpc{ApplicationService: applicationService}
|
||||
|
||||
grpcPresenter.RegisterAuthRpcServiceServer(grpcServer, &grpcApplicationServer)
|
||||
|
||||
log.Infof("Listening and serving gRPC on :%s with TLS", utils.GrpcPort)
|
||||
err = grpcServer.Serve(lis)
|
||||
if err != nil {
|
||||
log.Fatalf("Failure to start chaoscenter authentication GRPC server due to %v", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
AllowedOrigin string = "Access-Control-Allow-Origin"
|
||||
AllowedMethods string = "Access-Control-Allow-Methods"
|
||||
AllowedHeaders string = "Access-Control-Allow-Headers"
|
||||
AllowedCredentials string = "Access-Control-Allow-Credentials"
|
||||
)
|
||||
|
||||
func ValidateCors(allowedOrigins []string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
origin := c.GetHeader("Origin")
|
||||
if origin == "" {
|
||||
origin = c.Request.Host
|
||||
}
|
||||
|
||||
validOrigin := false
|
||||
for _, allowedOrigin := range allowedOrigins {
|
||||
match, err := regexp.MatchString(allowedOrigin, origin)
|
||||
if err == nil && match {
|
||||
validOrigin = true
|
||||
c.Writer.Header().Set(AllowedOrigin, origin)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !validOrigin {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Invalid origin",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Writer.Header().Set(AllowedMethods, strings.Join([]string{
|
||||
"GET",
|
||||
"POST",
|
||||
"PUT",
|
||||
"DELETE",
|
||||
"OPTIONS",
|
||||
}, ","))
|
||||
c.Writer.Header().Set(AllowedHeaders, "*")
|
||||
c.Writer.Header().Set(AllowedCredentials, "true")
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// JwtMiddleware is a Gin Middleware that authorises requests
|
||||
func JwtMiddleware(service services.ApplicationService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
const BearerSchema = "Bearer "
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
tokenString := authHeader[len(BearerSchema):]
|
||||
token, err := service.ValidateToken(tokenString)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
if token.Valid {
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
c.Set("username", claims["username"])
|
||||
c.Set("uid", claims["uid"])
|
||||
c.Set("role", claims["role"])
|
||||
c.Next()
|
||||
} else {
|
||||
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package middleware_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/middleware"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/mocks"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestJwtMiddleware(t *testing.T) {
|
||||
router := gin.Default()
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
router.Use(middleware.JwtMiddleware(mockService))
|
||||
|
||||
router.GET("/status", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Access Granted"})
|
||||
})
|
||||
|
||||
createRequest := func(token string) *httptest.ResponseRecorder {
|
||||
req := httptest.NewRequest("GET", "/status", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
w := httptest.NewRecorder()
|
||||
return w
|
||||
}
|
||||
|
||||
t.Run("Valid Token", func(t *testing.T) {
|
||||
wValid := createRequest("jwtstring")
|
||||
assert.Equal(t, http.StatusOK, wValid.Code)
|
||||
})
|
||||
|
||||
t.Run("Missing Authorization Header", func(t *testing.T) {
|
||||
wMissingHeader := httptest.NewRecorder()
|
||||
reqMissingHeader := httptest.NewRequest("GET", "/status", nil)
|
||||
router.ServeHTTP(wMissingHeader, reqMissingHeader)
|
||||
assert.Equal(t, http.StatusUnauthorized, wMissingHeader.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestJwtMiddleware_SetClaimsAndCallNextHandler(t *testing.T) {
|
||||
router := gin.Default()
|
||||
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
router.Use(middleware.JwtMiddleware(mockService))
|
||||
|
||||
router.GET("/status", func(c *gin.Context) {
|
||||
username, _ := c.Get("username")
|
||||
uid, _ := c.Get("uid")
|
||||
role, _ := c.Get("role")
|
||||
assert.Equal(t, "testuser", username)
|
||||
assert.Equal(t, "12345", uid)
|
||||
assert.Equal(t, "admin", role)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Access Granted"})
|
||||
})
|
||||
|
||||
mockService.On("ValidateToken", "jwtstring").Return(&jwt.Token{
|
||||
Valid: true,
|
||||
Claims: jwt.MapClaims{
|
||||
"username": "testuser",
|
||||
"uid": "12345",
|
||||
"role": "admin",
|
||||
},
|
||||
}, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/status", nil)
|
||||
req.Header.Set("Authorization", "Bearer jwtstring")
|
||||
router.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func TestJwtMiddleware_TokenValidationFailure(t *testing.T) {
|
||||
router := gin.Default()
|
||||
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
router.Use(middleware.JwtMiddleware(mockService))
|
||||
|
||||
router.GET("/status", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Access Granted"})
|
||||
})
|
||||
|
||||
mockService.On("ValidateToken", "invalidtoken").Return(&jwt.Token{
|
||||
Valid: false,
|
||||
}, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/status", nil)
|
||||
req.Header.Set("Authorization", "Bearer invalidtoken")
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
}
|
||||
|
||||
func TestJwtMiddleware_Error(t *testing.T) {
|
||||
router := gin.Default()
|
||||
|
||||
mockService := new(mocks.MockedApplicationService)
|
||||
router.Use(middleware.JwtMiddleware(mockService))
|
||||
|
||||
dummyToken := &jwt.Token{
|
||||
Raw: "DummyToken",
|
||||
Method: jwt.SigningMethodHS256,
|
||||
Header: map[string]interface{}{"alg": "HS256"},
|
||||
Claims: jwt.MapClaims{"foo": "bar"},
|
||||
Signature: "",
|
||||
Valid: false,
|
||||
}
|
||||
|
||||
mockService.On("ValidateToken", "dummy").Return(dummyToken, errors.New("mock error"))
|
||||
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("Authorization", "Bearer dummy")
|
||||
resp := httptest.NewRecorder()
|
||||
router.ServeHTTP(resp, req)
|
||||
|
||||
if status := resp.Code; status != utils.ErrorStatusCodes[utils.ErrUnauthorized] {
|
||||
t.Errorf("Handler returned wrong status code: got %v want %v", status, utils.ErrorStatusCodes[utils.ErrUnauthorized])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter/protos"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// ServerGrpc is a mock type for the ServerGrpc type
|
||||
type MockAuthRpcServiceClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockAuthRpcServiceClient) ValidateRequest(ctx context.Context, in *protos.ValidationRequest, opts ...grpc.CallOption) (*protos.ValidationResponse, error) {
|
||||
args := m.Called(ctx, in, opts)
|
||||
if vr, ok := args.Get(0).(*protos.ValidationResponse); ok {
|
||||
return vr, args.Error(1)
|
||||
}
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockAuthRpcServiceClient) GetProjectById(ctx context.Context, in *protos.GetProjectByIdRequest, opts ...grpc.CallOption) (*protos.GetProjectByIdResponse, error) {
|
||||
args := m.Called(ctx, in, opts)
|
||||
if gpir, ok := args.Get(0).(*protos.GetProjectByIdResponse); ok {
|
||||
return gpir, args.Error(1)
|
||||
}
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockAuthRpcServiceClient) GetUserById(ctx context.Context, in *protos.GetUserByIdRequest, opts ...grpc.CallOption) (*protos.GetUserByIdResponse, error) {
|
||||
args := m.Called(ctx, in, opts)
|
||||
if guir, ok := args.Get(0).(*protos.GetUserByIdResponse); ok {
|
||||
return guir, args.Error(1)
|
||||
}
|
||||
return nil, args.Error(1)
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/authConfig"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type MockedApplicationService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) IsAdministrator(user *entities.User) error {
|
||||
args := m.Called(user)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdatePassword(userPassword *entities.UserPassword, isAdminBeingReset bool) error {
|
||||
args := m.Called(userPassword, isAdminBeingReset)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) AddMember(projectID string, member *entities.Member) error {
|
||||
args := m.Called(projectID, member)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) LoginUser(user *entities.User) (*entities.User, error) {
|
||||
args := m.Called(user)
|
||||
return args.Get(0).(*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetUser(uid string) (*entities.User, error) {
|
||||
args := m.Called(uid)
|
||||
return args.Get(0).(*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetUsers() (*[]entities.User, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).(*[]entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) FindUsersByUID(uid []string) (*[]entities.User, error) {
|
||||
args := m.Called(uid)
|
||||
return args.Get(0).(*[]entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) FindUserByUsername(username string) (*entities.User, error) {
|
||||
args := m.Called(username)
|
||||
return args.Get(0).(*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) CheckPasswordHash(hash, password string) error {
|
||||
args := m.Called(hash, password)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) CreateUser(user *entities.User) (*entities.User, error) {
|
||||
args := m.Called(user)
|
||||
return args.Get(0).(*entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateUser(user *entities.UserDetails) error {
|
||||
args := m.Called(user)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateUserByQuery(filter bson.D, updateQuery bson.D) error {
|
||||
args := m.Called(filter, updateQuery)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateUserState(ctx context.Context, username string, isDeactivate bool, deactivateTime int64) error {
|
||||
args := m.Called(ctx, username, isDeactivate, deactivateTime)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) InviteUsers(invitedUsers []string) (*[]entities.User, error) {
|
||||
args := m.Called(invitedUsers)
|
||||
return args.Get(0).(*[]entities.User), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectByProjectID(projectID string) (*entities.Project, error) {
|
||||
args := m.Called(projectID)
|
||||
return args.Get(0).(*entities.Project), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjects(query bson.D) ([]*entities.Project, error) {
|
||||
args := m.Called(query)
|
||||
return args.Get(0).([]*entities.Project), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectsByUserID(request *entities.ListProjectRequest) (*entities.ListProjectResponse, error) {
|
||||
args := m.Called(request)
|
||||
return args.Get(0).(*entities.ListProjectResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectStats() ([]*entities.ProjectStats, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).([]*entities.ProjectStats), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) CreateProject(project *entities.Project) error {
|
||||
args := m.Called(project)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) RemoveInvitation(projectID, userID string, invitation entities.Invitation) error {
|
||||
args := m.Called(projectID, userID, invitation)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateInvite(projectID, userID string, invitation entities.Invitation, role *entities.MemberRole) error {
|
||||
args := m.Called(projectID, userID, invitation, role)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateProjectName(projectID, projectName string) error {
|
||||
args := m.Called(projectID, projectName)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateMemberRole(projectID, userID string, role *entities.MemberRole) error {
|
||||
args := m.Called(projectID, userID, role)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetAggregateProjects(pipeline mongo.Pipeline, opts *options.AggregateOptions) (*mongo.Cursor, error) {
|
||||
args := m.Called(pipeline, opts)
|
||||
return args.Get(0).(*mongo.Cursor), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateProjectState(ctx context.Context, userID string, deactivateTime int64, isDeactivate bool) error {
|
||||
args := m.Called(ctx, userID, deactivateTime, isDeactivate)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetOwnerProjectIDs(ctx context.Context, userID string) ([]*entities.Project, error) {
|
||||
args := m.Called(ctx, userID)
|
||||
return args.Get(0).([]*entities.Project), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectRole(projectID, userID string) (*entities.MemberRole, error) {
|
||||
args := m.Called(projectID, userID)
|
||||
return args.Get(0).(*entities.MemberRole), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectMembers(projectID, state string) ([]*entities.Member, error) {
|
||||
args := m.Called(projectID, state)
|
||||
return args.Get(0).([]*entities.Member), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetProjectOwners(projectID string) ([]*entities.Member, error) {
|
||||
args := m.Called(projectID)
|
||||
return args.Get(0).([]*entities.Member), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) ListInvitations(userID string, invitationState entities.Invitation) ([]*entities.Project, error) {
|
||||
args := m.Called(userID, invitationState)
|
||||
return args.Get(0).([]*entities.Project), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) RevokeToken(tokenString string) error {
|
||||
args := m.Called(tokenString)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) ValidateToken(encodedToken string) (*jwt.Token, error) {
|
||||
args := m.Called(encodedToken)
|
||||
return args.Get(0).(*jwt.Token), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetSignedJWT(user *entities.User, jwtSecret string) (string, error) {
|
||||
args := m.Called(user, jwtSecret)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) CreateApiToken(user *entities.User, request entities.ApiTokenInput) (string, error) {
|
||||
args := m.Called(user, request)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetApiTokensByUserID(userID string) ([]entities.ApiToken, error) {
|
||||
args := m.Called(userID)
|
||||
return args.Get(0).([]entities.ApiToken), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) DeleteApiToken(token string) error {
|
||||
args := m.Called(token)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) ListCollection() ([]string, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).([]string), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) ListDataBase() ([]string, error) {
|
||||
args := m.Called()
|
||||
return args.Get(0).([]string), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateStateTransaction(userRequest entities.UpdateUserState) error {
|
||||
args := m.Called(userRequest)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) RbacValidator(userID, resourceID string, rules []string, invitationStatus string) error {
|
||||
args := m.Called(userID, resourceID, rules, invitationStatus)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) DeleteProject(projectID string) error {
|
||||
args := m.Called(projectID)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) CreateConfig(config authConfig.AuthConfig) error {
|
||||
args := m.Called(config)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) GetConfig(key string) (*authConfig.AuthConfig, error) {
|
||||
args := m.Called(key)
|
||||
return args.Get(0).(*authConfig.AuthConfig), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockedApplicationService) UpdateConfig(ctx context.Context, key string, value interface{}) error {
|
||||
args := m.Called(ctx, key, value)
|
||||
return args.Error(0)
|
||||
}
|
|
@ -0,0 +1,774 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: authentication.proto
|
||||
|
||||
package protos
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// The validation function that checks if the user has the required permission over the project
|
||||
type ValidationRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Jwt string `protobuf:"bytes,1,opt,name=jwt,proto3" json:"jwt,omitempty"`
|
||||
ProjectId string `protobuf:"bytes,2,opt,name=projectId,proto3" json:"projectId,omitempty"`
|
||||
RequiredRoles []string `protobuf:"bytes,3,rep,name=requiredRoles,proto3" json:"requiredRoles,omitempty"`
|
||||
Invitation string `protobuf:"bytes,4,opt,name=invitation,proto3" json:"invitation,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ValidationRequest) Reset() {
|
||||
*x = ValidationRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_authentication_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ValidationRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ValidationRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ValidationRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_authentication_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ValidationRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ValidationRequest) Descriptor() ([]byte, []int) {
|
||||
return file_authentication_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ValidationRequest) GetJwt() string {
|
||||
if x != nil {
|
||||
return x.Jwt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ValidationRequest) GetProjectId() string {
|
||||
if x != nil {
|
||||
return x.ProjectId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ValidationRequest) GetRequiredRoles() []string {
|
||||
if x != nil {
|
||||
return x.RequiredRoles
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ValidationRequest) GetInvitation() string {
|
||||
if x != nil {
|
||||
return x.Invitation
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// The validation response that will contain the results of the validation request
|
||||
type ValidationResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
|
||||
IsValid bool `protobuf:"varint,2,opt,name=isValid,proto3" json:"isValid,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ValidationResponse) Reset() {
|
||||
*x = ValidationResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_authentication_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ValidationResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ValidationResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ValidationResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_authentication_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ValidationResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ValidationResponse) Descriptor() ([]byte, []int) {
|
||||
return file_authentication_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *ValidationResponse) GetError() string {
|
||||
if x != nil {
|
||||
return x.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ValidationResponse) GetIsValid() bool {
|
||||
if x != nil {
|
||||
return x.IsValid
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetProjectByIdRequest is the message struct for requesting project details by ID
|
||||
type GetProjectByIdRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ProjectID string `protobuf:"bytes,1,opt,name=projectID,proto3" json:"projectID,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdRequest) Reset() {
|
||||
*x = GetProjectByIdRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_authentication_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetProjectByIdRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetProjectByIdRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_authentication_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetProjectByIdRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetProjectByIdRequest) Descriptor() ([]byte, []int) {
|
||||
return file_authentication_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdRequest) GetProjectID() string {
|
||||
if x != nil {
|
||||
return x.ProjectID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ProjectMembers is the message struct that holds the details about the project members
|
||||
type ProjectMembers struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"`
|
||||
Username string `protobuf:"bytes,2,opt,name=userName,proto3" json:"userName,omitempty"`
|
||||
Role string `protobuf:"bytes,3,opt,name=role,proto3" json:"role,omitempty"`
|
||||
Email string `protobuf:"bytes,4,opt,name=email,proto3" json:"email,omitempty"`
|
||||
Invitation string `protobuf:"bytes,5,opt,name=invitation,proto3" json:"invitation,omitempty"`
|
||||
JoinedAt string `protobuf:"bytes,6,opt,name=joinedAt,proto3" json:"joinedAt,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ProjectMembers) Reset() {
|
||||
*x = ProjectMembers{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_authentication_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ProjectMembers) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ProjectMembers) ProtoMessage() {}
|
||||
|
||||
func (x *ProjectMembers) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_authentication_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ProjectMembers.ProtoReflect.Descriptor instead.
|
||||
func (*ProjectMembers) Descriptor() ([]byte, []int) {
|
||||
return file_authentication_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ProjectMembers) GetUid() string {
|
||||
if x != nil {
|
||||
return x.Uid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ProjectMembers) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ProjectMembers) GetRole() string {
|
||||
if x != nil {
|
||||
return x.Role
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ProjectMembers) GetEmail() string {
|
||||
if x != nil {
|
||||
return x.Email
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ProjectMembers) GetInvitation() string {
|
||||
if x != nil {
|
||||
return x.Invitation
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ProjectMembers) GetJoinedAt() string {
|
||||
if x != nil {
|
||||
return x.JoinedAt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetProjectByIdRequest is the message struct for response of project details by ID
|
||||
type GetProjectByIdResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Uid string `protobuf:"bytes,2,opt,name=uid,proto3" json:"uid,omitempty"`
|
||||
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Members []*ProjectMembers `protobuf:"bytes,4,rep,name=members,proto3" json:"members,omitempty"`
|
||||
State string `protobuf:"bytes,5,opt,name=state,proto3" json:"state,omitempty"`
|
||||
CreatedAt string `protobuf:"bytes,6,opt,name=createdAt,proto3" json:"createdAt,omitempty"`
|
||||
UpdatedAt string `protobuf:"bytes,7,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"`
|
||||
RemovedAt string `protobuf:"bytes,8,opt,name=removedAt,proto3" json:"removedAt,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdResponse) Reset() {
|
||||
*x = GetProjectByIdResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_authentication_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetProjectByIdResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetProjectByIdResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_authentication_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetProjectByIdResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetProjectByIdResponse) Descriptor() ([]byte, []int) {
|
||||
return file_authentication_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdResponse) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdResponse) GetUid() string {
|
||||
if x != nil {
|
||||
return x.Uid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdResponse) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdResponse) GetMembers() []*ProjectMembers {
|
||||
if x != nil {
|
||||
return x.Members
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdResponse) GetState() string {
|
||||
if x != nil {
|
||||
return x.State
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdResponse) GetCreatedAt() string {
|
||||
if x != nil {
|
||||
return x.CreatedAt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdResponse) GetUpdatedAt() string {
|
||||
if x != nil {
|
||||
return x.UpdatedAt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetProjectByIdResponse) GetRemovedAt() string {
|
||||
if x != nil {
|
||||
return x.RemovedAt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetUserByIdRequest is the message struct for requesting user details by ID
|
||||
type GetUserByIdRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
UserID string `protobuf:"bytes,1,opt,name=userID,proto3" json:"userID,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetUserByIdRequest) Reset() {
|
||||
*x = GetUserByIdRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_authentication_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetUserByIdRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetUserByIdRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetUserByIdRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_authentication_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetUserByIdRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetUserByIdRequest) Descriptor() ([]byte, []int) {
|
||||
return file_authentication_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *GetUserByIdRequest) GetUserID() string {
|
||||
if x != nil {
|
||||
return x.UserID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetUserByIdResponse is the message struct for response of user details by ID
|
||||
type GetUserByIdResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
|
||||
Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
|
||||
Role string `protobuf:"bytes,4,opt,name=role,proto3" json:"role,omitempty"`
|
||||
Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"`
|
||||
CreatedAt string `protobuf:"bytes,6,opt,name=createdAt,proto3" json:"createdAt,omitempty"`
|
||||
UpdatedAt string `protobuf:"bytes,7,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"`
|
||||
DeactivatedAt string `protobuf:"bytes,8,opt,name=deactivatedAt,proto3" json:"deactivatedAt,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetUserByIdResponse) Reset() {
|
||||
*x = GetUserByIdResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_authentication_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetUserByIdResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetUserByIdResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetUserByIdResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_authentication_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetUserByIdResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetUserByIdResponse) Descriptor() ([]byte, []int) {
|
||||
return file_authentication_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *GetUserByIdResponse) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetUserByIdResponse) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetUserByIdResponse) GetEmail() string {
|
||||
if x != nil {
|
||||
return x.Email
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetUserByIdResponse) GetRole() string {
|
||||
if x != nil {
|
||||
return x.Role
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetUserByIdResponse) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetUserByIdResponse) GetCreatedAt() string {
|
||||
if x != nil {
|
||||
return x.CreatedAt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetUserByIdResponse) GetUpdatedAt() string {
|
||||
if x != nil {
|
||||
return x.UpdatedAt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GetUserByIdResponse) GetDeactivatedAt() string {
|
||||
if x != nil {
|
||||
return x.DeactivatedAt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_authentication_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_authentication_proto_rawDesc = []byte{
|
||||
0x0a, 0x14, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x89,
|
||||
0x01, 0x0a, 0x11, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6a, 0x77, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x03, 0x6a, 0x77, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
|
||||
0x74, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
|
||||
0x52, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x71,
|
||||
0x75, 0x69, 0x72, 0x65, 0x64, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e,
|
||||
0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
|
||||
0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x44, 0x0a, 0x12, 0x56, 0x61,
|
||||
0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x73, 0x56, 0x61, 0x6c, 0x69,
|
||||
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64,
|
||||
0x22, 0x35, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x79,
|
||||
0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f,
|
||||
0x6a, 0x65, 0x63, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72,
|
||||
0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x44, 0x22, 0xa4, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x6a,
|
||||
0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x75, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x75, 0x73, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61,
|
||||
0x69, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x41, 0x74, 0x18, 0x06,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x41, 0x74, 0x22, 0xf0,
|
||||
0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x79, 0x49,
|
||||
0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x30, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63,
|
||||
0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72,
|
||||
0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74,
|
||||
0x65, 0x64, 0x41, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61,
|
||||
0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
|
||||
0x41, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
|
||||
0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x41, 0x74,
|
||||
0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x41,
|
||||
0x74, 0x22, 0x2c, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x42, 0x79, 0x49, 0x64,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49,
|
||||
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x44, 0x22,
|
||||
0xe1, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x42, 0x79, 0x49, 0x64, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c,
|
||||
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x06,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12,
|
||||
0x1c, 0x0a, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x07, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x24, 0x0a,
|
||||
0x0d, 0x64, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x08,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x65, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65,
|
||||
0x64, 0x41, 0x74, 0x32, 0xf9, 0x01, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x52, 0x70, 0x63, 0x53,
|
||||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61,
|
||||
0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x56, 0x61,
|
||||
0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x51, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
|
||||
0x42, 0x79, 0x49, 0x64, 0x12, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47, 0x65,
|
||||
0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47, 0x65, 0x74,
|
||||
0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72,
|
||||
0x42, 0x79, 0x49, 0x64, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47, 0x65,
|
||||
0x74, 0x55, 0x73, 0x65, 0x72, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65,
|
||||
0x72, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42,
|
||||
0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_authentication_proto_rawDescOnce sync.Once
|
||||
file_authentication_proto_rawDescData = file_authentication_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_authentication_proto_rawDescGZIP() []byte {
|
||||
file_authentication_proto_rawDescOnce.Do(func() {
|
||||
file_authentication_proto_rawDescData = protoimpl.X.CompressGZIP(file_authentication_proto_rawDescData)
|
||||
})
|
||||
return file_authentication_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_authentication_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_authentication_proto_goTypes = []interface{}{
|
||||
(*ValidationRequest)(nil), // 0: protos.ValidationRequest
|
||||
(*ValidationResponse)(nil), // 1: protos.ValidationResponse
|
||||
(*GetProjectByIdRequest)(nil), // 2: protos.GetProjectByIdRequest
|
||||
(*ProjectMembers)(nil), // 3: protos.ProjectMembers
|
||||
(*GetProjectByIdResponse)(nil), // 4: protos.GetProjectByIdResponse
|
||||
(*GetUserByIdRequest)(nil), // 5: protos.GetUserByIdRequest
|
||||
(*GetUserByIdResponse)(nil), // 6: protos.GetUserByIdResponse
|
||||
}
|
||||
var file_authentication_proto_depIdxs = []int32{
|
||||
3, // 0: protos.GetProjectByIdResponse.members:type_name -> protos.ProjectMembers
|
||||
0, // 1: protos.authRpcService.ValidateRequest:input_type -> protos.ValidationRequest
|
||||
2, // 2: protos.authRpcService.GetProjectById:input_type -> protos.GetProjectByIdRequest
|
||||
5, // 3: protos.authRpcService.GetUserById:input_type -> protos.GetUserByIdRequest
|
||||
1, // 4: protos.authRpcService.ValidateRequest:output_type -> protos.ValidationResponse
|
||||
4, // 5: protos.authRpcService.GetProjectById:output_type -> protos.GetProjectByIdResponse
|
||||
6, // 6: protos.authRpcService.GetUserById:output_type -> protos.GetUserByIdResponse
|
||||
4, // [4:7] is the sub-list for method output_type
|
||||
1, // [1:4] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_authentication_proto_init() }
|
||||
func file_authentication_proto_init() {
|
||||
if File_authentication_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_authentication_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ValidationRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_authentication_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ValidationResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_authentication_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetProjectByIdRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_authentication_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ProjectMembers); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_authentication_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetProjectByIdResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_authentication_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetUserByIdRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_authentication_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetUserByIdResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_authentication_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 7,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_authentication_proto_goTypes,
|
||||
DependencyIndexes: file_authentication_proto_depIdxs,
|
||||
MessageInfos: file_authentication_proto_msgTypes,
|
||||
}.Build()
|
||||
File_authentication_proto = out.File
|
||||
file_authentication_proto_rawDesc = nil
|
||||
file_authentication_proto_goTypes = nil
|
||||
file_authentication_proto_depIdxs = nil
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
syntax = "proto3";
|
||||
package protos;
|
||||
option go_package = "./protos";
|
||||
|
||||
|
||||
// The validation function that checks if the user has the required permission over the project
|
||||
message ValidationRequest{
|
||||
string jwt = 1;
|
||||
string projectId = 2;
|
||||
repeated string requiredRoles = 3;
|
||||
string invitation = 4 ;
|
||||
}
|
||||
|
||||
// The validation response that will contain the results of the validation request
|
||||
message ValidationResponse{
|
||||
string error = 1;
|
||||
bool isValid = 2;
|
||||
}
|
||||
|
||||
// GetProjectByIdRequest is the message struct for requesting project details by ID
|
||||
message GetProjectByIdRequest {
|
||||
string projectID = 1;
|
||||
}
|
||||
|
||||
//ProjectMembers is the message struct that holds the details about the project members
|
||||
message ProjectMembers {
|
||||
string uid = 1;
|
||||
string userName = 2;
|
||||
string role = 3;
|
||||
string email = 4;
|
||||
string invitation = 5;
|
||||
string joinedAt = 6;
|
||||
|
||||
}
|
||||
|
||||
// GetProjectByIdRequest is the message struct for response of project details by ID
|
||||
message GetProjectByIdResponse{
|
||||
string id = 1;
|
||||
string uid = 2;
|
||||
string name = 3;
|
||||
repeated ProjectMembers members = 4;
|
||||
string state = 5;
|
||||
string createdAt = 6;
|
||||
string updatedAt = 7;
|
||||
string removedAt = 8;
|
||||
}
|
||||
|
||||
// GetUserByIdRequest is the message struct for requesting user details by ID
|
||||
message GetUserByIdRequest {
|
||||
string userID = 1;
|
||||
}
|
||||
|
||||
// GetUserByIdResponse is the message struct for response of user details by ID
|
||||
message GetUserByIdResponse{
|
||||
string id = 1;
|
||||
string username = 2;
|
||||
string email = 3;
|
||||
string role=4;
|
||||
string name = 5;
|
||||
string createdAt = 6;
|
||||
string updatedAt = 7;
|
||||
string deactivatedAt=8;
|
||||
}
|
||||
|
||||
// Service definition for the authentication RPC Service
|
||||
service authRpcService{
|
||||
rpc ValidateRequest(ValidationRequest) returns (ValidationResponse) {}
|
||||
rpc GetProjectById (GetProjectByIdRequest) returns (GetProjectByIdResponse) {}
|
||||
rpc GetUserById (GetUserByIdRequest) returns (GetUserByIdResponse) {}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc v3.21.12
|
||||
// source: authentication.proto
|
||||
|
||||
package protos
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
const (
|
||||
AuthRpcService_ValidateRequest_FullMethodName = "/protos.authRpcService/ValidateRequest"
|
||||
AuthRpcService_GetProjectById_FullMethodName = "/protos.authRpcService/GetProjectById"
|
||||
AuthRpcService_GetUserById_FullMethodName = "/protos.authRpcService/GetUserById"
|
||||
)
|
||||
|
||||
// AuthRpcServiceClient is the client API for AuthRpcService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type AuthRpcServiceClient interface {
|
||||
ValidateRequest(ctx context.Context, in *ValidationRequest, opts ...grpc.CallOption) (*ValidationResponse, error)
|
||||
GetProjectById(ctx context.Context, in *GetProjectByIdRequest, opts ...grpc.CallOption) (*GetProjectByIdResponse, error)
|
||||
GetUserById(ctx context.Context, in *GetUserByIdRequest, opts ...grpc.CallOption) (*GetUserByIdResponse, error)
|
||||
}
|
||||
|
||||
type authRpcServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewAuthRpcServiceClient(cc grpc.ClientConnInterface) AuthRpcServiceClient {
|
||||
return &authRpcServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *authRpcServiceClient) ValidateRequest(ctx context.Context, in *ValidationRequest, opts ...grpc.CallOption) (*ValidationResponse, error) {
|
||||
out := new(ValidationResponse)
|
||||
err := c.cc.Invoke(ctx, AuthRpcService_ValidateRequest_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authRpcServiceClient) GetProjectById(ctx context.Context, in *GetProjectByIdRequest, opts ...grpc.CallOption) (*GetProjectByIdResponse, error) {
|
||||
out := new(GetProjectByIdResponse)
|
||||
err := c.cc.Invoke(ctx, AuthRpcService_GetProjectById_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authRpcServiceClient) GetUserById(ctx context.Context, in *GetUserByIdRequest, opts ...grpc.CallOption) (*GetUserByIdResponse, error) {
|
||||
out := new(GetUserByIdResponse)
|
||||
err := c.cc.Invoke(ctx, AuthRpcService_GetUserById_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AuthRpcServiceServer is the server API for AuthRpcService service.
|
||||
// All implementations must embed UnimplementedAuthRpcServiceServer
|
||||
// for forward compatibility
|
||||
type AuthRpcServiceServer interface {
|
||||
ValidateRequest(context.Context, *ValidationRequest) (*ValidationResponse, error)
|
||||
GetProjectById(context.Context, *GetProjectByIdRequest) (*GetProjectByIdResponse, error)
|
||||
GetUserById(context.Context, *GetUserByIdRequest) (*GetUserByIdResponse, error)
|
||||
mustEmbedUnimplementedAuthRpcServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedAuthRpcServiceServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedAuthRpcServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedAuthRpcServiceServer) ValidateRequest(context.Context, *ValidationRequest) (*ValidationResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ValidateRequest not implemented")
|
||||
}
|
||||
func (UnimplementedAuthRpcServiceServer) GetProjectById(context.Context, *GetProjectByIdRequest) (*GetProjectByIdResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetProjectById not implemented")
|
||||
}
|
||||
func (UnimplementedAuthRpcServiceServer) GetUserById(context.Context, *GetUserByIdRequest) (*GetUserByIdResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetUserById not implemented")
|
||||
}
|
||||
func (UnimplementedAuthRpcServiceServer) mustEmbedUnimplementedAuthRpcServiceServer() {}
|
||||
|
||||
// UnsafeAuthRpcServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to AuthRpcServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeAuthRpcServiceServer interface {
|
||||
mustEmbedUnimplementedAuthRpcServiceServer()
|
||||
}
|
||||
|
||||
func RegisterAuthRpcServiceServer(s grpc.ServiceRegistrar, srv AuthRpcServiceServer) {
|
||||
s.RegisterService(&AuthRpcService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _AuthRpcService_ValidateRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ValidationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthRpcServiceServer).ValidateRequest(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthRpcService_ValidateRequest_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthRpcServiceServer).ValidateRequest(ctx, req.(*ValidationRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthRpcService_GetProjectById_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetProjectByIdRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthRpcServiceServer).GetProjectById(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthRpcService_GetProjectById_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthRpcServiceServer).GetProjectById(ctx, req.(*GetProjectByIdRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthRpcService_GetUserById_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetUserByIdRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthRpcServiceServer).GetUserById(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthRpcService_GetUserById_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthRpcServiceServer).GetUserById(ctx, req.(*GetUserByIdRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// AuthRpcService_ServiceDesc is the grpc.ServiceDesc for AuthRpcService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var AuthRpcService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "protos.authRpcService",
|
||||
HandlerType: (*AuthRpcServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "ValidateRequest",
|
||||
Handler: _AuthRpcService_ValidateRequest_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetProjectById",
|
||||
Handler: _AuthRpcService_GetProjectById_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetUserById",
|
||||
Handler: _AuthRpcService_GetUserById_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "authentication.proto",
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.6.1
|
||||
// source: project.proto
|
||||
|
||||
package protos
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
wrappers "github.com/golang/protobuf/ptypes/wrappers"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// The request message containing the projectID
|
||||
type ProjectInitializationRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ProjectID string `protobuf:"bytes,1,opt,name=projectID,proto3" json:"projectID,omitempty"`
|
||||
Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ProjectInitializationRequest) Reset() {
|
||||
*x = ProjectInitializationRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_project_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ProjectInitializationRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ProjectInitializationRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ProjectInitializationRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_project_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ProjectInitializationRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ProjectInitializationRequest) Descriptor() ([]byte, []int) {
|
||||
return file_project_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ProjectInitializationRequest) GetProjectID() string {
|
||||
if x != nil {
|
||||
return x.ProjectID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ProjectInitializationRequest) GetRole() string {
|
||||
if x != nil {
|
||||
return x.Role
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_project_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_project_proto_rawDesc = []byte{
|
||||
0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72,
|
||||
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x50, 0x0a, 0x1c, 0x50, 0x72, 0x6f, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65,
|
||||
0x63, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a,
|
||||
0x65, 0x63, 0x74, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x32, 0x62, 0x0a, 0x07, 0x50, 0x72, 0x6f,
|
||||
0x6a, 0x65, 0x63, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69,
|
||||
0x7a, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61,
|
||||
0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x42, 0x0a, 0x5a,
|
||||
0x08, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_project_proto_rawDescOnce sync.Once
|
||||
file_project_proto_rawDescData = file_project_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_project_proto_rawDescGZIP() []byte {
|
||||
file_project_proto_rawDescOnce.Do(func() {
|
||||
file_project_proto_rawDescData = protoimpl.X.CompressGZIP(file_project_proto_rawDescData)
|
||||
})
|
||||
return file_project_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_project_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_project_proto_goTypes = []interface{}{
|
||||
(*ProjectInitializationRequest)(nil), // 0: protos.ProjectInitializationRequest
|
||||
(*wrappers.BoolValue)(nil), // 1: google.protobuf.BoolValue
|
||||
}
|
||||
var file_project_proto_depIdxs = []int32{
|
||||
0, // 0: protos.Project.InitializeProject:input_type -> protos.ProjectInitializationRequest
|
||||
1, // 1: protos.Project.InitializeProject:output_type -> google.protobuf.BoolValue
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_project_proto_init() }
|
||||
func file_project_proto_init() {
|
||||
if File_project_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_project_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ProjectInitializationRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_project_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_project_proto_goTypes,
|
||||
DependencyIndexes: file_project_proto_depIdxs,
|
||||
MessageInfos: file_project_proto_msgTypes,
|
||||
}.Build()
|
||||
File_project_proto = out.File
|
||||
file_project_proto_rawDesc = nil
|
||||
file_project_proto_goTypes = nil
|
||||
file_project_proto_depIdxs = nil
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
syntax = "proto3";
|
||||
package protos;
|
||||
|
||||
option go_package = "./protos";
|
||||
|
||||
import "google/protobuf/wrappers.proto";
|
||||
|
||||
// The project service definition.
|
||||
service Project {
|
||||
// Initialize project by adding instances for the required db collections
|
||||
rpc InitializeProject (ProjectInitializationRequest) returns (google.protobuf.BoolValue) {
|
||||
}
|
||||
}
|
||||
|
||||
// The request message containing the projectID
|
||||
message ProjectInitializationRequest {
|
||||
string projectID = 1;
|
||||
string role = 2;
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package protos
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
wrappers "github.com/golang/protobuf/ptypes/wrappers"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// ProjectClient is the client API for Project service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type ProjectClient interface {
|
||||
// Initialize project by adding instances for the required db collections
|
||||
InitializeProject(ctx context.Context, in *ProjectInitializationRequest, opts ...grpc.CallOption) (*wrappers.BoolValue, error)
|
||||
}
|
||||
|
||||
type projectClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewProjectClient(cc grpc.ClientConnInterface) ProjectClient {
|
||||
return &projectClient{cc}
|
||||
}
|
||||
|
||||
func (c *projectClient) InitializeProject(ctx context.Context, in *ProjectInitializationRequest, opts ...grpc.CallOption) (*wrappers.BoolValue, error) {
|
||||
out := new(wrappers.BoolValue)
|
||||
err := c.cc.Invoke(ctx, "/protos.Project/InitializeProject", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ProjectServer is the server API for Project service.
|
||||
// All implementations must embed UnimplementedProjectServer
|
||||
// for forward compatibility
|
||||
type ProjectServer interface {
|
||||
// Initialize project by adding instances for the required db collections
|
||||
InitializeProject(context.Context, *ProjectInitializationRequest) (*wrappers.BoolValue, error)
|
||||
mustEmbedUnimplementedProjectServer()
|
||||
}
|
||||
|
||||
// UnimplementedProjectServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedProjectServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedProjectServer) InitializeProject(context.Context, *ProjectInitializationRequest) (*wrappers.BoolValue, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method InitializeProject not implemented")
|
||||
}
|
||||
func (UnimplementedProjectServer) mustEmbedUnimplementedProjectServer() {}
|
||||
|
||||
// UnsafeProjectServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to ProjectServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeProjectServer interface {
|
||||
mustEmbedUnimplementedProjectServer()
|
||||
}
|
||||
|
||||
func RegisterProjectServer(s grpc.ServiceRegistrar, srv ProjectServer) {
|
||||
s.RegisterService(&Project_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _Project_InitializeProject_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ProjectInitializationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ProjectServer).InitializeProject(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/protos.Project/InitializeProject",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ProjectServer).InitializeProject(ctx, req.(*ProjectInitializationRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Project_ServiceDesc is the grpc.ServiceDesc for Project service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Project_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "protos.Project",
|
||||
HandlerType: (*ProjectServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "InitializeProject",
|
||||
Handler: _Project_InitializeProject_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "project.proto",
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package presenter
|
||||
|
||||
import "github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
|
||||
|
||||
// ErrorResponseStruct defines the structure for error responses
|
||||
type ErrorResponseStruct struct {
|
||||
Error string `json:"error"`
|
||||
ErrorDescription string `json:"errorDescription"`
|
||||
}
|
||||
|
||||
// CreateErrorResponse is a helper function that creates a ErrorResponseStruct
|
||||
func CreateErrorResponse(appError utils.AppError) *ErrorResponseStruct {
|
||||
return &ErrorResponseStruct{
|
||||
Error: appError.Error(),
|
||||
ErrorDescription: utils.ErrorDescriptions[appError],
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CapabilitiesRouter creates all the required routes for exposing capabilities.
|
||||
func CapabilitiesRouter(router *gin.Engine) {
|
||||
router.GET("/capabilities", rest.GetCapabilities())
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// DexRouter creates all the required routes for OAuth purposes.
|
||||
func DexRouter(router *gin.Engine, service services.ApplicationService) {
|
||||
router.GET("/dex/login", rest.DexLogin())
|
||||
router.GET("/dex/callback", rest.DexCallback(service))
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func MiscRouter(router *gin.Engine, service services.ApplicationService) {
|
||||
router.GET("/status", rest.Status(service))
|
||||
router.GET("/readiness", rest.Readiness(service))
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/middleware"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ProjectRouter creates all the required routes for project related purposes.
|
||||
func ProjectRouter(router *gin.Engine, service services.ApplicationService) {
|
||||
router.Use(middleware.JwtMiddleware(service))
|
||||
router.GET("/get_project/:project_id", rest.GetProject(service))
|
||||
router.GET("/get_project_members/:project_id/:state", rest.GetActiveProjectMembers(service))
|
||||
router.GET("/get_project_owners/:project_id", rest.GetActiveProjectOwners(service))
|
||||
router.GET("/get_user_with_project/:username", rest.GetUserWithProject(service))
|
||||
router.GET("/get_owner_projects", rest.GetOwnerProjects(service))
|
||||
router.GET("/get_project_role/:project_id", rest.GetProjectRole(service))
|
||||
router.GET("/list_projects", rest.GetProjectsByUserID(service))
|
||||
router.GET("/get_projects_stats", rest.GetProjectStats(service))
|
||||
router.GET("/list_invitations_with_filters/:invitation_state", rest.ListInvitations(service))
|
||||
router.POST("/create_project", rest.CreateProject(service))
|
||||
router.POST("/send_invitation", rest.SendInvitation(service))
|
||||
router.POST("/accept_invitation", rest.AcceptInvitation(service))
|
||||
router.POST("/decline_invitation", rest.DeclineInvitation(service))
|
||||
router.POST("/remove_invitation", rest.RemoveInvitation(service))
|
||||
router.POST("/leave_project", rest.LeaveProject(service))
|
||||
router.POST("/update_project_name", rest.UpdateProjectName(service))
|
||||
router.POST("/update_member_role", rest.UpdateMemberRole(service))
|
||||
router.POST("/delete_project/:project_id", rest.DeleteProject(service))
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/handlers/rest"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/middleware"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// UserRouter creates all the required routes for user authentications purposes.
|
||||
func UserRouter(router *gin.Engine, service services.ApplicationService) {
|
||||
router.POST("/login", rest.LoginUser(service))
|
||||
router.POST("/logout", rest.LogoutUser(service))
|
||||
router.Use(middleware.JwtMiddleware(service))
|
||||
router.GET("/token/:uid", rest.GetApiTokens(service))
|
||||
router.POST("/create_token", rest.CreateApiToken(service))
|
||||
router.POST("/remove_token", rest.DeleteApiToken(service))
|
||||
router.POST("/update/password", rest.UpdatePassword(service))
|
||||
router.POST("/reset/password", rest.ResetPassword(service))
|
||||
router.POST("/create_user", rest.CreateUser(service))
|
||||
router.POST("/update/details", rest.UpdateUser(service))
|
||||
router.GET("/get_user/:uid", rest.GetUser(service))
|
||||
router.GET("/users", rest.FetchUsers(service))
|
||||
router.GET("/invite_users/:project_id", rest.InviteUsers(service))
|
||||
router.POST("/update/state", rest.UpdateUserState(service))
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package types
|
||||
|
||||
type MemberState string
|
||||
|
||||
const (
|
||||
MemberStateActive MemberState = "active"
|
||||
MemberStateInactive MemberState = "inactive"
|
||||
)
|
||||
|
||||
const (
|
||||
ProjectName = "projectName"
|
||||
SortField = "sortField"
|
||||
Ascending = "sortAscending"
|
||||
CreatedByMe = "createdByMe"
|
||||
Page = "page"
|
||||
Limit = "limit"
|
||||
)
|
|
@ -0,0 +1,208 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/types"
|
||||
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func GetProjectFilters(c *gin.Context) *entities.ListProjectRequest {
|
||||
var request entities.ListProjectRequest
|
||||
|
||||
uID, exists := c.Get("uid")
|
||||
if exists {
|
||||
request.UserID = uID.(string)
|
||||
}
|
||||
|
||||
// Initialize request.Filter and request.Sort if they are nil
|
||||
if request.Filter == nil {
|
||||
request.Filter = &entities.ListProjectInputFilter{}
|
||||
}
|
||||
if request.Sort == nil {
|
||||
request.Sort = &entities.SortInput{}
|
||||
}
|
||||
|
||||
// filters
|
||||
createdByMeStr := c.Query(types.CreatedByMe)
|
||||
if createdByMeStr != "" {
|
||||
createdByMe, err := strconv.ParseBool(createdByMeStr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil
|
||||
}
|
||||
request.Filter.CreatedByMe = &createdByMe
|
||||
}
|
||||
|
||||
projectNameStr := c.Query(types.ProjectName)
|
||||
|
||||
if projectNameStr != "" {
|
||||
request.Filter.ProjectName = &projectNameStr
|
||||
}
|
||||
|
||||
// sorts
|
||||
var sortField entities.ProjectSortingField
|
||||
sortFieldStr := c.Query(types.SortField)
|
||||
|
||||
// Convert the string value to the appropriate type
|
||||
switch sortFieldStr {
|
||||
case "name":
|
||||
sortField = entities.ProjectSortingFieldName
|
||||
case "time":
|
||||
sortField = entities.ProjectSortingFieldTime
|
||||
default:
|
||||
sortField = entities.ProjectSortingFieldTime
|
||||
}
|
||||
|
||||
// Now assign the converted value to the sort field
|
||||
request.Sort.Field = &sortField
|
||||
|
||||
ascendingStr := c.Query(types.Ascending)
|
||||
if ascendingStr != "" {
|
||||
ascending, err := strconv.ParseBool(ascendingStr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil
|
||||
}
|
||||
request.Sort.Ascending = &ascending
|
||||
}
|
||||
|
||||
// pagination
|
||||
// Extract page and limit from query parameters
|
||||
pageStr := c.Query(types.Page)
|
||||
limitStr := c.Query(types.Limit)
|
||||
|
||||
// Convert strings to integers
|
||||
page, err := strconv.Atoi(pageStr)
|
||||
if err != nil {
|
||||
// Handle error if conversion fails
|
||||
// For example, set a default value or return an error response
|
||||
page = 0 // Setting a default value of 1
|
||||
}
|
||||
|
||||
limit, err := strconv.Atoi(limitStr)
|
||||
if err != nil {
|
||||
// Handle error if conversion fails
|
||||
// For example, set a default value or return an error response
|
||||
limit = 15 // Setting a default value of 15
|
||||
}
|
||||
|
||||
pagination := entities.Pagination{
|
||||
Page: page,
|
||||
Limit: limit,
|
||||
}
|
||||
|
||||
request.Pagination = &pagination
|
||||
|
||||
return &request
|
||||
}
|
||||
|
||||
func CreateMatchStage(userID string) bson.D {
|
||||
return bson.D{
|
||||
{"$match", bson.D{
|
||||
{"is_removed", false},
|
||||
{"members", bson.D{
|
||||
{"$elemMatch", bson.D{
|
||||
{"user_id", userID},
|
||||
{"invitation", bson.D{
|
||||
{"$nin", bson.A{
|
||||
string(entities.PendingInvitation),
|
||||
string(entities.DeclinedInvitation),
|
||||
string(entities.ExitedProject),
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func CreateFilterStages(filter *entities.ListProjectInputFilter, userID string) []bson.D {
|
||||
var stages []bson.D
|
||||
|
||||
if filter == nil {
|
||||
return stages
|
||||
}
|
||||
|
||||
if filter.CreatedByMe != nil {
|
||||
if *filter.CreatedByMe {
|
||||
stages = append(stages, bson.D{
|
||||
{"$match", bson.D{
|
||||
{"created_by.user_id", bson.M{"$eq": userID}},
|
||||
}},
|
||||
})
|
||||
} else {
|
||||
stages = append(stages, bson.D{
|
||||
{"$match", bson.D{
|
||||
{"created_by.user_id", bson.M{"$ne": userID}},
|
||||
}},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if filter.ProjectName != nil {
|
||||
stages = append(stages, bson.D{
|
||||
{"$match", bson.D{
|
||||
{"name", bson.D{
|
||||
{"$regex", primitive.Regex{Pattern: *filter.ProjectName, Options: "i"}},
|
||||
}},
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
return stages
|
||||
}
|
||||
|
||||
func CreateSortStage(sort *entities.SortInput) bson.D {
|
||||
if sort == nil || sort.Field == nil {
|
||||
return bson.D{}
|
||||
}
|
||||
|
||||
var sortField string
|
||||
switch *sort.Field {
|
||||
case entities.ProjectSortingFieldTime:
|
||||
sortField = "updated_at"
|
||||
case entities.ProjectSortingFieldName:
|
||||
sortField = "name"
|
||||
default:
|
||||
sortField = "updated_at"
|
||||
}
|
||||
|
||||
sortDirection := -1
|
||||
if sort.Ascending != nil && *sort.Ascending {
|
||||
sortDirection = 1
|
||||
}
|
||||
|
||||
return bson.D{
|
||||
{"$sort", bson.D{
|
||||
{sortField, sortDirection},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func CreatePaginationStage(pagination *entities.Pagination) []bson.D {
|
||||
var stages []bson.D
|
||||
if pagination != nil {
|
||||
page := pagination.Page
|
||||
limit := pagination.Limit
|
||||
// upper limit of 50 to prevent exceeding max limit 16mb
|
||||
if pagination.Limit > 50 {
|
||||
limit = 50
|
||||
}
|
||||
stages = append(stages, bson.D{
|
||||
{"$skip", page * limit},
|
||||
})
|
||||
stages = append(stages, bson.D{
|
||||
{"$limit", limit},
|
||||
})
|
||||
} else {
|
||||
stages = append(stages, bson.D{
|
||||
{"$limit", 10},
|
||||
})
|
||||
}
|
||||
return stages
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue