Removing old-style kapow.

This commit is contained in:
Roberto Abdelkader Martínez Pérez
2019-04-26 07:45:33 +02:00
parent 63756ad784
commit ea02a50536
13 changed files with 0 additions and 1367 deletions
-10
View File
@@ -1,10 +0,0 @@
FROM python:3.7.3-alpine3.8
# Install Dependences
RUN pip install pipenv
COPY Pipfile Pipfile.lock ./
RUN pipenv install --system --deploy && rm Pipfile Pipfile.lock
# Install kapow!
COPY kapow.py /usr/sbin/kapow
RUN chmod a+x /usr/sbin/kapow
-19
View File
@@ -1,19 +0,0 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
sphinx = "*"
ipython = "*"
ipdb = "*"
pylint = "*"
[packages]
pyparsing = "*"
aiohttp = "*"
click = "*"
aiofiles = "*"
[requires]
python_version = "3.7"
Generated
-538
View File
@@ -1,538 +0,0 @@
{
"_meta": {
"hash": {
"sha256": "fc23d2e0b455dc19dab220e38b1b8689a09c8aebd5b6657b06ec906462f5388a"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"aiofiles": {
"hashes": [
"sha256:021ea0ba314a86027c166ecc4b4c07f2d40fc0f4b3a950d1868a0f2571c2bbee",
"sha256:1e644c2573f953664368de28d2aa4c89dfd64550429d0c27c4680ccd3aa4985d"
],
"index": "pypi",
"version": "==0.4.0"
},
"aiohttp": {
"hashes": [
"sha256:00d198585474299c9c3b4f1d5de1a576cc230d562abc5e4a0e81d71a20a6ca55",
"sha256:0155af66de8c21b8dba4992aaeeabf55503caefae00067a3b1139f86d0ec50ed",
"sha256:09654a9eca62d1bd6d64aa44db2498f60a5c1e0ac4750953fdd79d5c88955e10",
"sha256:199f1d106e2b44b6dacdf6f9245493c7d716b01d0b7fbe1959318ba4dc64d1f5",
"sha256:296f30dedc9f4b9e7a301e5cc963012264112d78a1d3094cd83ef148fdf33ca1",
"sha256:368ed312550bd663ce84dc4b032a962fcb3c7cae099dbbd48663afc305e3b939",
"sha256:40d7ea570b88db017c51392349cf99b7aefaaddd19d2c78368aeb0bddde9d390",
"sha256:629102a193162e37102c50713e2e31dc9a2fe7ac5e481da83e5bb3c0cee700aa",
"sha256:6d5ec9b8948c3d957e75ea14d41e9330e1ac3fed24ec53766c780f82805140dc",
"sha256:87331d1d6810214085a50749160196391a712a13336cd02ce1c3ea3d05bcf8d5",
"sha256:9a02a04bbe581c8605ac423ba3a74999ec9d8bce7ae37977a3d38680f5780b6d",
"sha256:9c4c83f4fa1938377da32bc2d59379025ceeee8e24b89f72fcbccd8ca22dc9bf",
"sha256:9cddaff94c0135ee627213ac6ca6d05724bfe6e7a356e5e09ec57bd3249510f6",
"sha256:a25237abf327530d9561ef751eef9511ab56fd9431023ca6f4803f1994104d72",
"sha256:a5cbd7157b0e383738b8e29d6e556fde8726823dae0e348952a61742b21aeb12",
"sha256:a97a516e02b726e089cffcde2eea0d3258450389bbac48cbe89e0f0b6e7b0366",
"sha256:acc89b29b5f4e2332d65cd1b7d10c609a75b88ef8925d487a611ca788432dfa4",
"sha256:b05bd85cc99b06740aad3629c2585bda7b83bd86e080b44ba47faf905fdf1300",
"sha256:c2bec436a2b5dafe5eaeb297c03711074d46b6eb236d002c13c42f25c4a8ce9d",
"sha256:cc619d974c8c11fe84527e4b5e1c07238799a8c29ea1c1285149170524ba9303",
"sha256:d4392defd4648badaa42b3e101080ae3313e8f4787cb517efd3f5b8157eaefd6",
"sha256:e1c3c582ee11af7f63a34a46f0448fca58e59889396ffdae1f482085061a2889"
],
"index": "pypi",
"version": "==3.5.4"
},
"async-timeout": {
"hashes": [
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
"version": "==3.0.1"
},
"attrs": {
"hashes": [
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
],
"version": "==19.1.0"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"click": {
"hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
],
"index": "pypi",
"version": "==7.0"
},
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
],
"version": "==2.8"
},
"multidict": {
"hashes": [
"sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f",
"sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3",
"sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef",
"sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b",
"sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73",
"sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc",
"sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3",
"sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd",
"sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351",
"sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941",
"sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d",
"sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1",
"sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b",
"sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a",
"sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3",
"sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7",
"sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0",
"sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0",
"sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014",
"sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5",
"sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036",
"sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d",
"sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a",
"sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce",
"sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1",
"sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a",
"sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9",
"sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7",
"sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b"
],
"version": "==4.5.2"
},
"pyparsing": {
"hashes": [
"sha256:1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a",
"sha256:9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03"
],
"index": "pypi",
"version": "==2.4.0"
},
"yarl": {
"hashes": [
"sha256:024ecdc12bc02b321bc66b41327f930d1c2c543fa9a561b39861da9388ba7aa9",
"sha256:2f3010703295fbe1aec51023740871e64bb9664c789cba5a6bdf404e93f7568f",
"sha256:3890ab952d508523ef4881457c4099056546593fa05e93da84c7250516e632eb",
"sha256:3e2724eb9af5dc41648e5bb304fcf4891adc33258c6e14e2a7414ea32541e320",
"sha256:5badb97dd0abf26623a9982cd448ff12cb39b8e4c94032ccdedf22ce01a64842",
"sha256:73f447d11b530d860ca1e6b582f947688286ad16ca42256413083d13f260b7a0",
"sha256:7ab825726f2940c16d92aaec7d204cfc34ac26c0040da727cf8ba87255a33829",
"sha256:b25de84a8c20540531526dfbb0e2d2b648c13fd5dd126728c496d7c3fea33310",
"sha256:c6e341f5a6562af74ba55205dbd56d248daf1b5748ec48a0200ba227bb9e33f4",
"sha256:c9bb7c249c4432cd47e75af3864bc02d26c9594f49c82e2a28624417f0ae63b8",
"sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1"
],
"version": "==1.3.0"
}
},
"develop": {
"alabaster": {
"hashes": [
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
"sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
],
"version": "==0.7.12"
},
"astroid": {
"hashes": [
"sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4",
"sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4"
],
"version": "==2.2.5"
},
"babel": {
"hashes": [
"sha256:6778d85147d5d85345c14a26aada5e478ab04e39b078b0745ee6870c2b5cf669",
"sha256:8cba50f48c529ca3fa18cf81fa9403be176d374ac4d60738b839122dfaaa3d23"
],
"version": "==2.6.0"
},
"backcall": {
"hashes": [
"sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
"sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
],
"version": "==0.1.0"
},
"certifi": {
"hashes": [
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
"sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"
],
"version": "==2019.3.9"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"decorator": {
"hashes": [
"sha256:86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de",
"sha256:f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"
],
"version": "==4.4.0"
},
"docutils": {
"hashes": [
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274",
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"
],
"version": "==0.14"
},
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
],
"version": "==2.8"
},
"imagesize": {
"hashes": [
"sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8",
"sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5"
],
"version": "==1.1.0"
},
"ipdb": {
"hashes": [
"sha256:dce2112557edfe759742ca2d0fee35c59c97b0cc7a05398b791079d78f1519ce"
],
"index": "pypi",
"version": "==0.12"
},
"ipython": {
"hashes": [
"sha256:b038baa489c38f6d853a3cfc4c635b0cda66f2864d136fe8f40c1a6e334e2a6b",
"sha256:f5102c1cd67e399ec8ea66bcebe6e3968ea25a8977e53f012963e5affeb1fe38"
],
"index": "pypi",
"version": "==7.4.0"
},
"ipython-genutils": {
"hashes": [
"sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
"sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
],
"version": "==0.2.0"
},
"isort": {
"hashes": [
"sha256:01cb7e1ca5e6c5b3f235f0385057f70558b70d2f00320208825fa62887292f43",
"sha256:268067462aed7eb2a1e237fcb287852f22077de3fb07964e87e00f829eea2d1a"
],
"version": "==4.3.17"
},
"jedi": {
"hashes": [
"sha256:2bb0603e3506f708e792c7f4ad8fc2a7a9d9c2d292a358fbbd58da531695595b",
"sha256:2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c"
],
"version": "==0.13.3"
},
"jinja2": {
"hashes": [
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
],
"version": "==2.10.1"
},
"lazy-object-proxy": {
"hashes": [
"sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33",
"sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39",
"sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019",
"sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088",
"sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b",
"sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e",
"sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6",
"sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b",
"sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5",
"sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff",
"sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd",
"sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7",
"sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff",
"sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d",
"sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2",
"sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35",
"sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4",
"sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514",
"sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252",
"sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109",
"sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f",
"sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c",
"sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92",
"sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577",
"sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d",
"sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d",
"sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f",
"sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a",
"sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b"
],
"version": "==1.3.1"
},
"markupsafe": {
"hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
],
"version": "==1.1.1"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
],
"version": "==0.6.1"
},
"packaging": {
"hashes": [
"sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af",
"sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3"
],
"version": "==19.0"
},
"parso": {
"hashes": [
"sha256:17cc2d7a945eb42c3569d4564cdf49bde221bc2b552af3eca9c1aad517dcdd33",
"sha256:2e9574cb12e7112a87253e14e2c380ce312060269d04bd018478a3c92ea9a376"
],
"version": "==0.4.0"
},
"pexpect": {
"hashes": [
"sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1",
"sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"
],
"markers": "sys_platform != 'win32'",
"version": "==4.7.0"
},
"pickleshare": {
"hashes": [
"sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca",
"sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"
],
"version": "==0.7.5"
},
"prompt-toolkit": {
"hashes": [
"sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780",
"sha256:2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1",
"sha256:977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"
],
"version": "==2.0.9"
},
"ptyprocess": {
"hashes": [
"sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
"sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
],
"version": "==0.6.0"
},
"pygments": {
"hashes": [
"sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
"sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
],
"version": "==2.3.1"
},
"pylint": {
"hashes": [
"sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09",
"sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1"
],
"index": "pypi",
"version": "==2.3.1"
},
"pyparsing": {
"hashes": [
"sha256:1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a",
"sha256:9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03"
],
"index": "pypi",
"version": "==2.4.0"
},
"pytz": {
"hashes": [
"sha256:303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda",
"sha256:d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141"
],
"version": "==2019.1"
},
"requests": {
"hashes": [
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
],
"version": "==2.21.0"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
},
"snowballstemmer": {
"hashes": [
"sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128",
"sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89"
],
"version": "==1.2.1"
},
"sphinx": {
"hashes": [
"sha256:423280646fb37944dd3c85c58fb92a20d745793a9f6c511f59da82fa97cd404b",
"sha256:de930f42600a4fef993587633984cc5027dedba2464bcf00ddace26b40f8d9ce"
],
"index": "pypi",
"version": "==2.0.1"
},
"sphinxcontrib-applehelp": {
"hashes": [
"sha256:edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897",
"sha256:fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d"
],
"version": "==1.0.1"
},
"sphinxcontrib-devhelp": {
"hashes": [
"sha256:6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34",
"sha256:9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981"
],
"version": "==1.0.1"
},
"sphinxcontrib-htmlhelp": {
"hashes": [
"sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422",
"sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7"
],
"version": "==1.0.2"
},
"sphinxcontrib-jsmath": {
"hashes": [
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
],
"version": "==1.0.1"
},
"sphinxcontrib-qthelp": {
"hashes": [
"sha256:513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20",
"sha256:79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f"
],
"version": "==1.0.2"
},
"sphinxcontrib-serializinghtml": {
"hashes": [
"sha256:c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227",
"sha256:db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768"
],
"version": "==1.1.3"
},
"traitlets": {
"hashes": [
"sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
"sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"
],
"version": "==4.3.2"
},
"typed-ast": {
"hashes": [
"sha256:04894d268ba6eab7e093d43107869ad49e7b5ef40d1a94243ea49b352061b200",
"sha256:16616ece19daddc586e499a3d2f560302c11f122b9c692bc216e821ae32aa0d0",
"sha256:252fdae740964b2d3cdfb3f84dcb4d6247a48a6abe2579e8029ab3be3cdc026c",
"sha256:2af80a373af123d0b9f44941a46df67ef0ff7a60f95872412a145f4500a7fc99",
"sha256:2c88d0a913229a06282b285f42a31e063c3bf9071ff65c5ea4c12acb6977c6a7",
"sha256:2ea99c029ebd4b5a308d915cc7fb95b8e1201d60b065450d5d26deb65d3f2bc1",
"sha256:3d2e3ab175fc097d2a51c7a0d3fda442f35ebcc93bb1d7bd9b95ad893e44c04d",
"sha256:4766dd695548a15ee766927bf883fb90c6ac8321be5a60c141f18628fb7f8da8",
"sha256:56b6978798502ef66625a2e0f80cf923da64e328da8bbe16c1ff928c70c873de",
"sha256:5cddb6f8bce14325b2863f9d5ac5c51e07b71b462361fd815d1d7706d3a9d682",
"sha256:644ee788222d81555af543b70a1098f2025db38eaa99226f3a75a6854924d4db",
"sha256:64cf762049fc4775efe6b27161467e76d0ba145862802a65eefc8879086fc6f8",
"sha256:68c362848d9fb71d3c3e5f43c09974a0ae319144634e7a47db62f0f2a54a7fa7",
"sha256:6c1f3c6f6635e611d58e467bf4371883568f0de9ccc4606f17048142dec14a1f",
"sha256:b213d4a02eec4ddf622f4d2fbc539f062af3788d1f332f028a2e19c42da53f15",
"sha256:bb27d4e7805a7de0e35bd0cb1411bc85f807968b2b0539597a49a23b00a622ae",
"sha256:c9d414512eaa417aadae7758bc118868cd2396b0e6138c1dd4fda96679c079d3",
"sha256:f0937165d1e25477b01081c4763d2d9cdc3b18af69cb259dd4f640c9b900fe5e",
"sha256:fb96a6e2c11059ecf84e6741a319f93f683e440e341d4489c9b161eca251cf2a",
"sha256:fc71d2d6ae56a091a8d94f33ec9d0f2001d1cb1db423d8b4355debfe9ce689b7"
],
"markers": "implementation_name == 'cpython'",
"version": "==1.3.4"
},
"urllib3": {
"hashes": [
"sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0",
"sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3"
],
"version": "==1.24.2"
},
"wcwidth": {
"hashes": [
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
],
"version": "==0.1.7"
},
"wrapt": {
"hashes": [
"sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533"
],
"version": "==1.11.1"
}
}
}
-3
View File
@@ -1,3 +0,0 @@
POST /eval {
(@raw:request/body) > @fifo:response/stream/lines
}
-4
View File
@@ -1,4 +0,0 @@
FROM kapow
RUN apk add nmap
COPY nmap.pow .
CMD kapow nmap.pow
-3
View File
@@ -1,3 +0,0 @@
POST /list/{ip} {
nmap -sL @value:request/match/ip > @fifo:response/body
}
-37
View File
@@ -1,37 +0,0 @@
GET /list/files {
ls -la @value:request/params/path > @fifo:response/body
}
GET /list/processes {
ps -aux > @fifo:response/body
}
GET /show/cpuinfo {
cat /proc/cpuinfo > @fifo:response/body
}
GET /show/memory {
free -m > @fifo:response/body
}
GET /show/disk {
df -h > @fifo:response/body
}
GET /show/connections {
ss -pluton > @fifo:response/body
}
GET /show/mounts {
mount > @fifo:response/body
}
GET /tail/dmesg {
echo "text/plain" > @fifo:response/header/Content-Type
dmesg -w > @fifo:response/stream/lines
}
GET /tail/journal {
echo "text/plain" > @fifo:response/header/Content-Type
journalctl -f > @fifo:response/stream/lines
}
-31
View File
@@ -1,31 +0,0 @@
POST /convert/{from}/pdf {
tmpfile=$(mktemp --suffix=.pdf)
pandoc --from=@value:request/match/from \
--to=pdf \
--output=${tmpfile} \
-t latex \
@file:request/body
if [ $? -eq 0 ]; then
cat ${tmpfile} > @fifo:response/body
echo "application/pdf" > @fifo:response/header/Content-Type
echo 200 > @fifo:response/status
else
echo 500 > @fifo:response/status
fi
rm -f ${tmpfile}
}
POST /convert/{from}/{to} {
pandoc --from=@value:request/match/from \
--to=@value:request/match/to \
--output=@fifo:response/body \
@file:request/body
}
GET /formats/input {
pandoc --list-input-formats > @fifo:response/body
}
GET /formats/output {
pandoc --list-output-formats | grep -v pdf > @fifo:response/body
}
-41
View File
@@ -1,41 +0,0 @@
<html>
<head>
<title>PDF Editor</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/siimple/dist/siimple.min.css">
<head>
<body>
<div class="siimple-content siimple-content--extra-large">
<div class="siimple-grid">
<div class="siimple-grid-row">
<div class="siimple-grid-col siimple-grid-col--12">
<div class="siimple-grid-col siimple-grid-col--6">
<div class="siimple-form">
<form action="/editor/pdf" method="post" target="result" id="editor">
<div class="siimple-form-title">AWYSIWYG PDF Editor</div>
<div class="siimple-form-field">
<div class="siimple-form-field-label">InputFormat</div>
<select name="from">
<option value="markdown">Markdown</option>
<option value="rst">ReStructuredText</option>
</select>
</div>
<div class="siimple-form-field">
<div class="siimple-form-field-label">InputFormat</div>
<textarea class="siimple-textarea siimple-textarea--fluid" rows="25" name="content">Example text</textarea>
</div>
<div class="siimple-form-field">
<div class="siimple-btn siimple-btn--blue" onclick="document.getElementById('editor').submit();">Preview!</div>
</div>
</form>
</div>
</div>
<div class="siimple-grid-col siimple-grid-col--6">
<iframe name="result" src="" style="height: 100%; width: 100%;"></iframe>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
-19
View File
@@ -1,19 +0,0 @@
POST /editor/pdf {
tmpfile=$(mktemp --suffix=.pdf)
pandoc --from=@value:request/form/from \
--to=pdf \
--output=${tmpfile} \
-t latex \
@file:request/form/content
if [ $? -eq 0 ]; then
cat ${tmpfile} > @fifo:response/body
echo "application/pdf" > @fifo:response/header/Content-Type
echo 200 > @fifo:response/status
else
echo 500 > @fifo:response/status
fi
rm -f ${tmpfile}
}
GET / = index.html
-28
View File
@@ -1,28 +0,0 @@
GET / {
echo "text/html" > @fifo:response/header/Content-Type
cat > @fifo:response/body <<-HTML
<html>
<body>
<a href='javascript: Array.from(document.querySelectorAll("a")).filter(x => x.href.indexOf("magnet") != -1 ).map(x => x.href = "http://localhost:8080/save/magnet?link="+encodeURI(x.href))'>Add me to your bookmarks!</a>
</body>
</html>
HTML
}
GET /save/magnet {
set -x
link=@value:request/param/link
[ -z $link ] && echo 400 > @fifo:response/status && exit 0
watch_folder=/tmp
cd $watch_folder
[[ "$link" =~ xt=urn:btih:([^&/]+) ]] || exit;
echo "d10:magnet-uri${#link}:${link}e" > "meta-${BASH_REMATCH[1]}.torrent"
echo 302 > @fifo:response/status
echo "/torrent/list" > @fifo:response/header/Location
}
GET /torrent/list {
echo "..." > @fifo:response/body
}
-504
View File
@@ -1,504 +0,0 @@
#!/usr/bin/env python
"""
A Kapow! interpreter written in Python.
"""
from dataclasses import dataclass
from shlex import quote as shell_quote
from string import Template
import asyncio
import contextlib
import io
import logging
import os
import sys
import tempfile
import threading
import traceback
from aiohttp import web
from pyparsing import alphas, nums, White
from pyparsing import LineStart, LineEnd, SkipTo
from pyparsing import Literal, Combine, Word, Suppress
from pyparsing import OneOrMore, Optional, delimitedList
import aiofiles
import click
log = logging.getLogger('kapow')
########################################################################
# Parser #
########################################################################
COMMENT = (Literal('#') + SkipTo(LineEnd()))(name="comment")
# Method
METHOD = (Literal('GET')
| Literal('POST')
| Literal('PUT')
| Literal('DELETE')
| Literal('PATCH'))
MULTI_METHOD = delimitedList(METHOD, delim="|", combine=True)
METHOD_SPEC = Combine(Literal('*') | MULTI_METHOD)
# Pattern
REGEX = Word(alphas + nums + '\\+*,.[]-_')(name="regex")
SYMBOL = Word(alphas)(name="symbol")
P_PATTERN = Combine('/{' + SYMBOL + Optional(':' + REGEX) + '}')
P_PATH = Word('/', alphas + nums + '$-_.+!*\'(),')
URLPATTERN = Combine(OneOrMore(P_PATTERN | P_PATH))(name="urlpattern")
# Body
BODY = (Suppress('{')
+ SkipTo(Combine(LineStart() + '}' + LineEnd()))(name="body"))
# Endpoint head
ENDPOINT = (Optional(METHOD_SPEC + Suppress(White()),
default='*')(name="method")
+ URLPATTERN
+ Suppress(White()))
# Endpoint bodies
CODE_EP = (ENDPOINT + BODY)(name="code_ep")
PATH_EP = (ENDPOINT + '=' + SkipTo(LineEnd())(name="path"))(name="path_ep")
KAPOW_PROGRAM = CODE_EP | PATH_EP | COMMENT
########################################################################
# Resources #
########################################################################
@dataclass
class ResourceManager:
"""A resource exposed to the subshell."""
#: Kapow resource representation
kapow_repr: str
#: Representation of the resource that can be understood by the shell
shell_repr: str
#: Coroutine capable of managing the resource internally
coro: object
#: Path to readed fifo. Needs to be written for coro to release.
#: XXX: Use proper fifo async instead
fifo_path: str = None
#: Fifo direction 'read'/'write'
fifo_direction: str = None
async def get_value(context, path):
"""Return the value of an http resource."""
def nrd(n):
"""Return the nrd element in a path."""
return path.split('/')[n]
try:
if path == 'request/method':
return context['request'].method.encode('utf-8')
elif path == 'request/path':
return context['request'].path.encode('utf-8')
elif path.startswith('request/match'):
return context['request'].match_info[nrd(2)].encode('utf-8')
elif path.startswith('request/param'):
return context['request'].rel_url.query[nrd(2)].encode('utf-8')
elif path.startswith('request/header'):
return context['request'].headers[nrd(2)].encode('utf-8')
elif path.startswith('request/cookie'):
return context['request'].cookies[nrd(2)].encode('utf-8')
elif path.startswith('request/form'):
return (await context['request'].post())[nrd(2)].encode('utf-8')
elif path.startswith('request/file'):
name = nrd(2)
content = nrd(3) # filename / content
field = (await context['request'].post())[name]
if content == 'filename':
try:
return field.filename.encode('utf-8')
except:
return b''
elif content == 'content':
try:
return field.file.read()
except:
return b''
else:
raise ValueError(f'Unknown content type {content!r}')
elif path == 'request/body':
return await context['request'].read()
else:
raise ValueError(f'Unknown path {path!r}')
except KeyError:
return b''
async def set_value(context, path, value):
"""
Write to an http resource.
File-like resources like `body` get write() calls so they have
append semantics. Non file-like resources are just set.
"""
if not value:
return
def nrd(n):
return path.split('/')[n]
if path == 'response/status':
context['response_status'] = int(value.decode('utf-8'))
elif path == 'response/body':
context['response_body'].write(value)
elif path == 'response/body':
context['response_stream'].write(value)
elif path.startswith('response/header/'):
clean = value.rstrip(b'\n').decode('utf-8')
context['response_headers'][nrd(2)] = clean
elif path.startswith('response/cookie/'):
clean = value.rstrip(b'\n').decode('utf-8')
context['response_cookies'][nrd(2)] = clean
else:
raise ValueError(f'Unknown path {path!r}')
def is_readable(path):
return path.startswith('request/')
def is_writable(path):
return path.startswith('response/')
def get_manager(resource, context):
"""
Return an async context manager capable of manage the given
resource.
"""
try:
view, path = resource.split(':')
except:
log.error(f"Invalid resource %r", resource)
raise
@contextlib.asynccontextmanager
async def manager():
"""
Manage the given `resource` as an async context manager.
This context manager has different behavior depending on the
`view` and/or `path` of the resource.
As a context manager it has three sections:
- Before `yield`: Prepare, if needed, the physical resource on
disk.
- `yield`: Return a `ResourceManager` object containing the
shell representation of the object and the coroutine
consuming/generating the resource data.
- After `yield`: Cleanup any disk resource.
"""
if view == 'raw':
if not is_readable(path):
raise ValueError(f'Non-readable path "{path}".')
else:
value = await get_value(context, path)
yield ResourceManager(
kapow_repr=resource,
shell_repr=value.decode('utf-8'),
coro=asyncio.sleep(0))
elif view == 'value':
if not is_readable(path):
raise ValueError(f'Non-readable path "{path}".')
else:
value = await get_value(context, path)
yield ResourceManager(
kapow_repr=resource,
shell_repr=shell_quote(value.decode('utf-8')),
coro=asyncio.sleep(0))
elif view == 'fifo':
# No race condition here? Shut your ass!!
# https://stackoverflow.com/a/1430566
filename = tempfile.mktemp()
os.mkfifo(filename)
if path.startswith('response/stream'):
async def manage_fifo():
initialized = False
try:
async with aiofiles.open(filename, 'rb') as fifo:
while True:
if path.endswith('/lines'):
chunk = await fifo.readline()
else:
chunk = await fifo.read(1024*10)
if chunk:
if not initialized:
# Give a chance to other coroutines
# to write changes to response
# (headers, etc)
await asyncio.sleep(0)
response = web.StreamResponse(
status=200,
headers=context["response_headers"],
reason="OK")
for name, value in context["response_cookies"]:
response.set_cookie(name, value)
context["stream"] = response
await response.prepare(context["request"])
initialized = True
await response.write(chunk)
else:
break
finally:
os.unlink(filename)
else:
async def manage_fifo():
try:
if is_readable(path):
async with aiofiles.open(filename, 'wb') as fifo:
await fifo.write(await get_value(context, path))
elif is_writable(path):
async with aiofiles.open(filename, 'rb') as fifo:
buf = io.BytesIO()
while True:
chunk = await fifo.read(128)
if not chunk:
break
buf.write(chunk)
await set_value(context, path, buf.getvalue())
else:
raise RuntimeError('WTF!')
finally:
os.unlink(filename)
yield ResourceManager(
kapow_repr=resource,
shell_repr=shell_quote(filename),
coro=manage_fifo(),
fifo_path=filename,
fifo_direction='read' if is_readable(path) else 'write')
elif view == 'file':
with tempfile.NamedTemporaryFile(mode='w+b', buffering=0) as tmp:
if is_readable(path):
value = await get_value(context, path)
tmp.write(value)
tmp.flush()
yield ResourceManager(
kapow_repr=resource,
shell_repr=shell_quote(tmp.name),
coro=asyncio.sleep(0))
if is_writable(path):
tmp.seek(0)
await set_value(context, path, tmp.read())
else:
raise ValueError(f'Unknown view type {view}')
return manager
class KapowTemplate(Template):
"""Shell-code templating for @view:path variables substitution"""
delimiter = '@'
idpattern = r'(?a:[_a-z][_a-z0-9]*:[_a-z][-_a-z0-9/]*)'
async def run(self, context):
"""Run this template allocating and deallocating resources."""
async with contextlib.AsyncExitStack() as stack:
# Initialize all resources creating a mapping
resources = dict() # resource: (shell_repr, manager)
for match in self.pattern.findall(self.template):
delim, resource, *rest = match
if not resource: # When is braced
resource = rest[0]
if delim and not resource and rest == ['', '']:
# Escaped
continue
if resource not in resources:
try:
manager = get_manager(resource, context)
except:
log.error(f"Invalid match %r, %r, %r", delim, resource, rest)
raise
resources[resource] = await stack.enter_async_context(manager())
code = self.substitute(**{k: v.shell_repr
for k, v in resources.items()})
log.debug("Creating tasks")
manager_tasks = {asyncio.create_task(v.coro): v
for k, v in resources.items()}
await asyncio.sleep(0)
log.debug("Creating subprocess")
shell_task = await asyncio.create_subprocess_shell(
code,
executable=os.environ.get('SHELL', '/bin/sh'))
log.debug("Waiting for subprocess")
await shell_task.wait() # Run the subshell process
done, pending = await asyncio.wait(manager_tasks.keys(), timeout=0.1)
if pending:
for task in pending:
resource = manager_tasks[task]
if resource.fifo_path is not None:
log.debug(f"Trying to stop %s", resource.kapow_repr)
if resource.fifo_direction == 'write':
os.system(f"echo -n > {resource.fifo_path} &")
elif resource.fifo_direction == 'read':
os.system(f"cat {resource.fifo_path} > /dev/null &")
else:
raise ValueError("Unknown direction")
else:
log.debug(f"Non-fifo resource pending!! %s", resource.kapow_repr)
log.debug("Waiting for pending resources...")
await asyncio.wait(pending)
await asyncio.sleep(0)
def create_context(request):
"""Create a request context with default values."""
context = dict()
context["request"] = request
context["stream"] = None
context["response_body"] = io.BytesIO()
context["response_status"] = 200
context["response_headers"] = dict()
context["response_cookies"] = dict()
return context
async def response_from_context(context):
"""Return the appropia aiohttp response for a given context."""
if context["stream"] is not None:
await context["stream"].write_eof()
return context["stream"]
else:
body = context["response_body"].getvalue()
status = context["response_status"]
headers = context["response_headers"]
cookies = context["response_cookies"]
# Content-Type guessing (for demo only)
if "Content-Type" not in headers:
try:
body = body.decode("utf-8")
except UnicodeDecodeError:
pass
else:
headers["Content-Type"] = "text/plain"
response = web.Response(body=body, status=status, headers=headers)
for name, value in cookies.items():
response.set_cookie(name, value)
return response
def generate_endpoint(code, path=None):
"""Return an aiohttp-endpoint coroutine to run kapow `code`."""
async def endpoint(request):
context = create_context(request)
log.debug("Running endpoint %r", path)
try:
await KapowTemplate(code).run(context) # Will change context
except:
log.exception("Template crashed!")
log.debug("Endpoint finished, creating response %r", path)
response = await response_from_context(context)
log.debug("Responding %r", path)
return response
return endpoint
def path_server(path):
"""Return an aiohttp-endpoint coroutine to serve the file in `path`."""
# At initialization check
if not os.path.isfile(path):
raise NotImplementedError("Only files can be served.")
async def serve_path(request):
# Per request check
if os.path.isdir(path):
raise NotImplementedError("Cannot serve whole directories yet.")
return web.FileResponse(path)
return serve_path
########################################################################
# Webserver #
########################################################################
def register_code_endpoint(app, methods, pattern, code):
"""Register all needed endpoints for the defined endpoint code."""
endpoint = generate_endpoint(code, pattern)
for method in methods: # May be '*'
app.add_routes([web.route(method, pattern, endpoint)])
def register_path_endpoint(app, methods, pattern, path):
"""Register all needed endpoints for the defined file."""
for method in methods:
if method != 'GET':
raise ValueError("Invalid method for serving files.")
else:
app.add_routes([web.static(pattern, path)])
async def debug_tasks():
while True:
await asyncio.sleep(1)
log.debug("Tasks: %s | Threads: %s",
len(asyncio.Task.all_tasks()),
threading.active_count())
async def start_background_tasks(app):
app["debug_tasks"] = app.loop.create_task(debug_tasks())
@click.command()
@click.option('--expression', '-e')
@click.option('--verbose', '-v', count=True)
@click.argument('program', type=click.File(), required=False)
@click.pass_context
def main(ctx, program, verbose, expression):
"""Run the kapow server with the given command-line parameters."""
if program is None and expression is None:
program = sys.stdin
app = web.Application(client_max_size=1024*1024*1024)
if verbose == 0:
_print = lambda _: None
logging.basicConfig(stream=sys.stderr, level=logging.ERROR)
else:
_print = lambda s: print(s, file=sys.stderr)
if verbose == 1:
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
else:
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
if verbose > 2:
app.on_startup.append(start_background_tasks)
source = expression if program is None else program.read()
for ep, _, _ in KAPOW_PROGRAM.scanString(source):
if 'comment' in ep:
continue
methods = ep.method.asList()[0].split('|')
pattern = ''.join(ep.urlpattern)
if ep.body:
log.info(f"Registering [code] methods=%r pattern=%r", methods, pattern)
register_code_endpoint(app, methods, pattern, ep.body)
else:
log.info(f"Registering [path] methods=%r pattern=%r", methods, pattern)
register_path_endpoint(app, methods, pattern, ep.path)
web.run_app(app, print=_print)
if __name__ == '__main__':
main()
-130
View File
@@ -1,130 +0,0 @@
10-SECOND PROPOSAL
===================
*kapow* is a specialized language for marrying the **web** and the **shell**.
DESCRIPTION
===========
The **web** and the **shell** are two different beasts, both packed with history.
There are some concepts in HTTP and the shell that **resemble each other**.
```
+------------------------+-------------------------+
| HTTP | SHELL |
+--------------+------------------------+-------------------------+
| Input | POST form-encoding | Command line parameters |
| Parameters | GET parameters | Environment variables |
| | Headers | |
| | Serialized body (JSON) | |
+--------------+------------------------+-------------------------+
| Data Streams | Response/Request Body | Stdin/Stdout/Stderr |
| | Websocket | Input/Output files |
| | Uploaded files | |
+--------------+------------------------+-------------------------+
| Control | Status codes | Signals |
| | HTTP Methods | |
+--------------+------------------------+-------------------------+
```
Any tool designed to give an HTTP interface to an existing shell command **must map concepts of boths**. For example:
- "GET parameters" to "Command line parameters"
- "Headers" to "Environment variables"
- "Stdout" to "Response body"
*kapow* is not opinionated about the different ways you can map both worlds. Instead it provides a concise language used to express the mapping and a set of common defaults.
Why not tool...?
----------------
All the alternatives we found are **rigid** about how they match between HTTP and shell concepts.
* [shell2http](https://github.com/msoap/shell2http): HTTP-server to execute shell commands. Designed for development, prototyping or remote control. Settings through two command line arguments, path and shell command.
* [websocketd](https://github.com/joewalnes/websocketd): Turn any program that uses STDIN/STDOUT into a WebSocket server. Like inetd, but for WebSockets.
* [webhook](https://github.com/adnanh/webhook): webhook is a lightweight incoming webhook server to run shell commands.
* [gotty](https://github.com/yudai/gotty): GoTTY is a simple command line tool that turns your CLI tools into web applications. (For interactive commands only)
Tools with a rigid matching **can't evade** *[impedance mismatch](https://haacked.com/archive/2004/06/15/impedance-mismatch.aspx/)*. Resulting is an easy-to-use software, convenient in some scenarios but incapable in others.
Why not my good-old programming language...?
--------------------------------------------
* Boilerplate
* Custom code = More bugs
* Security issues (Command injection, etc)
* Dependency on developers
* "A programming language is low level when its programs require attention to the irrelevant" Alan Perlis
*kapow* aims to be halfway from one of the mentioned tools and a general programming language. A limited scripting language. Think of *awk*, 20 years later, for HTTP.
Example
-------
Imagine you want to improve your monitoring system to be able to check if a WiFi in a remote building is working. Suppose you already have a wireless capable linux server on that building.
What about exposing a wifi scan as a service?
```
$ iwlist scan | grep 'ESSID:"BBVA"'
```
The above command will list the visible WiFI networks and filter for the one we are interested in (**BBVA**); exiting with code **0** when found and **1** otherwise.
We want to signal the WiFI status with a meaningful HTTP status code: 200 for UP and 503 for DOWN.
So the only thing our service has to do is translate from the **command's exit code** to a meaningful **HTTP status code**.
Our *kapow* program (monitor.pow) should look **something like** this:
```
/monitor/wifi =
$(iwlist scan | grep 'ESSID:"BBVA')
| 0 = status 200 # OK
| 1 = status 503 # Service Unavailable
```
With this code, the user can run the service with:
```
$ kapow monitor.pow
```
And then:
1. *kapow* will open a port with an HTTP server serving the URI **/monitor/wifi**.
2. When requested `curl http://<server>/monitor/wifi`. The command is executed resulting in a WiFI scan.
3. The command's **exit code** is translated to an HTTP **status code** and returned.
USE CASES OR VALUE
==================
* Reuse 40+ years of existing computer programs as microservices (nanoservices?).
* Expose command line only tools as services. Eliminating the need of interactive SSH sessions.
* Fine-grained access control to CLI based on options/parameters
REQUIRED SKILLS
===============
- Knowledge of HTTP and the command line.
- Programming.
- Low-level stuff...
ESTIMATION
==========
- 2 weeks
OUTPUT
======
- A collection of **concrete** use cases, 5 to 10.
- A DSL design (over paper) to implement the use cases.
- A working **dirty** proof of concept.