diff options
| -rw-r--r-- | Pipfile | 19 | ||||
| -rw-r--r-- | Pipfile.lock | 337 | ||||
| -rw-r--r-- | README.md | 59 | ||||
| -rw-r--r-- | config.py | 35 | ||||
| -rw-r--r-- | docker-compose.yml | 40 | ||||
| -rw-r--r-- | docker/Dockerfile | 3 | ||||
| -rw-r--r-- | docker/Dockerfile.webapp | 25 | ||||
| -rw-r--r-- | rmq.py | 132 | ||||
| -rw-r--r-- | snekbox.py | 65 | ||||
| -rw-r--r-- | snekweb.py | 73 | ||||
| -rw-r--r-- | templates/index.html | 114 | ||||
| -rw-r--r-- | templates/result.html | 9 | ||||
| -rw-r--r-- | tests/test_snekbox.py | 20 | 
13 files changed, 243 insertions, 688 deletions
| @@ -4,15 +4,12 @@ verify_ssl = true  name = "pypi"  [packages] -pika = "*"  docker = "*" - -[dev-packages]  flask = "*" -flask-sockets = "*" -gevent = "==1.2.2" -gevent-websocket = "*" +gevent = "*"  gunicorn = "*" + +[dev-packages]  "flake8" = "*"  pytest = "*"  pytest-cov = "*" @@ -25,18 +22,10 @@ python_version = "3.6"  lint = "flake8"  test = "py.test tests --cov . --cov-report term-missing -v"  report = "py.test tests --cov . --cov-report=html" - -snekbox = "python snekbox.py" -snekweb = "gunicorn -w 2 -b 0.0.0.0:5000 --log-level debug -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker snekweb:app" - +snekbox = "gunicorn -w 2 -b 0.0.0.0:8060 snekbox:app"  buildbox = "docker build -t pythondiscord/snekbox:latest -f docker/Dockerfile ."  pushbox = "docker push pythondiscord/snekbox:latest" -  buildboxbase = "docker build -t pythondiscord/snekbox-base:latest -f docker/base.Dockerfile ."  pushboxbase = "docker push pythondiscord/snekbox-base:latest" -  buildci = "docker build -t pythondiscord/snekbox-ci:latest -f docker/ci.Dockerfile ."  pushci = "docker push pythondiscord/snekbox-ci:latest" - -buildweb = "docker build -t pythondiscord/snekboxweb:latest -f docker/Dockerfile.webapp ." -pushweb = "docker push pythondiscord/snekboxweb:latest" diff --git a/Pipfile.lock b/Pipfile.lock index d2f8198..ae63b8c 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@  {      "_meta": {          "hash": { -            "sha256": "db284676e7f7232ab8e2f8dbda2319fcb62fe648209c2ff8db78c6db058b7d1e" +            "sha256": "7b1debb4f15f17babadb882a0adc77f19a2e674100cf08d2675e2e5e8a9c8de3"          },          "pipfile-spec": 6,          "requires": { @@ -18,10 +18,10 @@      "default": {          "certifi": {              "hashes": [ -                "sha256:339dc09518b07e2fa7eda5450740925974815557727d6bd35d319c1524a04a4c", -                "sha256:6d58c986d22b038c8c0df30d639f23a3e6d172a05c3583e766f4c0b785c0986a" +                "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7", +                "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"              ], -            "version": "==2018.10.15" +            "version": "==2018.11.29"          },          "chardet": {              "hashes": [ @@ -30,80 +30,6 @@              ],              "version": "==3.0.4"          }, -        "docker": { -            "hashes": [ -                "sha256:145c673f531df772a957bd1ebc49fc5a366bcd55efa0e64bbd029f5cc7a1fd8e", -                "sha256:666611862edded75f6049893f779bff629fdcd4cd21ccf01d648626e709adb13" -            ], -            "index": "pypi", -            "version": "==3.6.0" -        }, -        "docker-pycreds": { -            "hashes": [ -                "sha256:0a941b290764ea7286bd77f54c0ace43b86a8acd6eb9ead3de9840af52384079", -                "sha256:8b0e956c8d206f832b06aa93a710ba2c3bcbacb5a314449c040b0b814355bbff" -            ], -            "version": "==0.3.0" -        }, -        "idna": { -            "hashes": [ -                "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", -                "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" -            ], -            "version": "==2.7" -        }, -        "pika": { -            "hashes": [ -                "sha256:035e4e46069a81d1135eed27cf74ef0fedf9a0a32285966717233529e9f69bae", -                "sha256:306145b8683e016d81aea996bcaefee648483fc5a9eb4694bb488f54df54a751" -            ], -            "index": "pypi", -            "version": "==0.12.0" -        }, -        "requests": { -            "hashes": [ -                "sha256:65b3a120e4329e33c9889db89c80976c5272f56ea92d3e74da8a463992e3ff54", -                "sha256:ea881206e59f41dbd0bd445437d792e43906703fff75ca8ff43ccdb11f33f263" -            ], -            "version": "==2.20.1" -        }, -        "six": { -            "hashes": [ -                "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", -                "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" -            ], -            "version": "==1.11.0" -        }, -        "urllib3": { -            "hashes": [ -                "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", -                "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" -            ], -            "version": "==1.24.1" -        }, -        "websocket-client": { -            "hashes": [ -                "sha256:8c8bf2d4f800c3ed952df206b18c28f7070d9e3dcbd6ca6291127574f57ee786", -                "sha256:e51562c91ddb8148e791f0155fdb01325d99bb52c4cdbb291aee7a3563fd0849" -            ], -            "version": "==0.54.0" -        } -    }, -    "develop": { -        "atomicwrites": { -            "hashes": [ -                "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", -                "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee" -            ], -            "version": "==1.2.1" -        }, -        "attrs": { -            "hashes": [ -                "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", -                "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb" -            ], -            "version": "==18.2.0" -        },          "click": {              "hashes": [                  "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", @@ -111,49 +37,20 @@              ],              "version": "==7.0"          }, -        "coverage": { +        "docker": {              "hashes": [ -                "sha256:09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f", -                "sha256:0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe", -                "sha256:0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d", -                "sha256:10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0", -                "sha256:1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607", -                "sha256:1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d", -                "sha256:2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b", -                "sha256:447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3", -                "sha256:46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e", -                "sha256:4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815", -                "sha256:510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36", -                "sha256:5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1", -                "sha256:5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14", -                "sha256:5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c", -                "sha256:6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794", -                "sha256:6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b", -                "sha256:77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840", -                "sha256:828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd", -                "sha256:85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82", -                "sha256:8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952", -                "sha256:a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389", -                "sha256:aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f", -                "sha256:ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4", -                "sha256:b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da", -                "sha256:bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647", -                "sha256:c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d", -                "sha256:d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42", -                "sha256:d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478", -                "sha256:da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b", -                "sha256:ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb", -                "sha256:ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9" +                "sha256:2840ffb9dc3ef6d00876bde476690278ab13fa1f8ba9127ef855ac33d00c3152", +                "sha256:5831256da3477723362bc71a8df07b8cd8493e4a4a60cebd45580483edbe48ae"              ], -            "version": "==4.5.2" +            "index": "pypi", +            "version": "==3.7.0"          }, -        "flake8": { +        "docker-pycreds": {              "hashes": [ -                "sha256:6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670", -                "sha256:c01f8a3963b3571a8e6bd7a4063359aff90749e160778e03817cd9b71c9e07d2" +                "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4", +                "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49"              ], -            "index": "pypi", -            "version": "==3.6.0" +            "version": "==0.4.0"          },          "flask": {              "hashes": [ @@ -163,48 +60,34 @@              "index": "pypi",              "version": "==1.0.2"          }, -        "flask-sockets": { -            "hashes": [ -                "sha256:072927da8edca0e81e024f5787e643c87d80b351b714de95d723becb30e0643b", -                "sha256:350a76d55f5889f64afd2ca9b32f262680b7960965f0830365576307d30cfe1e" -            ], -            "index": "pypi", -            "version": "==0.2.1" -        },          "gevent": {              "hashes": [ -                "sha256:0901975628790e8a57fc92bb7062e5b856edea48c8de9caf36cfda14eae07329", -                "sha256:1af93825db5753550fa8ff5ab2f2132e8733170b3f8d38347b34fa4a984cb624", -                "sha256:2ff045a91509c35664c27a849c8cbf742a227f587b7cdbc88301e9c85dcaedff", -                "sha256:35790f1a3c8e431ada3471b70bb2105050009ea4beb15cbe41b86bc716a7ffa9", -                "sha256:4791c8ae9c57d6f153354736e1ccab1e2baf6c8d9ae5a77a9ac90f41e2966b2d", -                "sha256:552719cec4721673b8c7d2f9de666e3f7591b9b182f801ecaef1c76e638052aa", -                "sha256:59e9237af027f8db85e5d78a9da2e328ae96f01d67a0d62abcecad3db7876908", -                "sha256:60109741377367eef8ded9283a1bf629621b73acaf3e1e8aac9d1a0f50fa0f05", -                "sha256:70558dd45c7a1f8046ba45792e489dd0f409bd8a3b7a0635ca9d3055223b3dff", -                "sha256:81cb24e0f7bd9888596364e8d8ed0d65c2547c84884c67bb46d956faeed67396", -                "sha256:833bebdc36bfeeedefc200ca9aee9b8eddd80f56b63ca1e886e18b97b1240edd", -                "sha256:8a710eddb3e9e5f22bdbd458b5f211b94f59409ecd6896f15b9fee2cba266a59", -                "sha256:9b492bb1a043540abb6e54fdb5537531e24962ca49c09f3b47dc4f9c37f6297c", -                "sha256:a16db4f56699ef07f0249b953ff949aae641e50b2bdc4710f11c0d8d9089b296", -                "sha256:a66cf99f08da65c501826a19e30f5a6e7ba942fdd79baba5ce2d51eebaa13444", -                "sha256:b67a10799923f9fed546ca5f8b93a2819c71a60132d7a97b4a13fbdab66b278a", -                "sha256:b7e0e6400c2f3ce78a9ae1cdd55b53166feedd003d60c033863881227129a4d3", -                "sha256:c9dd6534c46ed782e2d7236767cd07115cb29ce8670c2fc0794f264de9024fe0", -                "sha256:de13a8e378103af84a8bf6015ad1d2761d46f29b8393e8dd6d9bb7cb51bbb713", -                "sha256:deafd70d04ab62428d4e291e8e2c0fb22f38690e6a9f23a67ee6c304087634da", -                "sha256:df52e06a2754c2d905aad75a7dc06a732c804d9edbc87f06f47c8f483ba98bca" +                "sha256:0774babec518a24d9a7231d4e689931f31b332c4517a771e532002614e270a64", +                "sha256:0e1e5b73a445fe82d40907322e1e0eec6a6745ca3cea19291c6f9f50117bb7ea", +                "sha256:0ff2b70e8e338cf13bedf146b8c29d475e2a544b5d1fe14045aee827c073842c", +                "sha256:107f4232db2172f7e8429ed7779c10f2ed16616d75ffbe77e0e0c3fcdeb51a51", +                "sha256:14b4d06d19d39a440e72253f77067d27209c67e7611e352f79fe69e0f618f76e", +                "sha256:1b7d3a285978b27b469c0ff5fb5a72bcd69f4306dbbf22d7997d83209a8ba917", +                "sha256:1eb7fa3b9bd9174dfe9c3b59b7a09b768ecd496debfc4976a9530a3e15c990d1", +                "sha256:2711e69788ddb34c059a30186e05c55a6b611cb9e34ac343e69cf3264d42fe1c", +                "sha256:28a0c5417b464562ab9842dd1fb0cc1524e60494641d973206ec24d6ec5f6909", +                "sha256:3249011d13d0c63bea72d91cec23a9cf18c25f91d1f115121e5c9113d753fa12", +                "sha256:44089ed06a962a3a70e96353c981d628b2d4a2f2a75ea5d90f916a62d22af2e8", +                "sha256:4bfa291e3c931ff3c99a349d8857605dca029de61d74c6bb82bd46373959c942", +                "sha256:50024a1ee2cf04645535c5ebaeaa0a60c5ef32e262da981f4be0546b26791950", +                "sha256:53b72385857e04e7faca13c613c07cab411480822ac658d97fd8a4ddbaf715c8", +                "sha256:74b7528f901f39c39cdbb50cdf08f1a2351725d9aebaef212a29abfbb06895ee", +                "sha256:7d0809e2991c9784eceeadef01c27ee6a33ca09ebba6154317a257353e3af922", +                "sha256:896b2b80931d6b13b5d9feba3d4eebc67d5e6ec54f0cf3339d08487d55d93b0e", +                "sha256:8d9ec51cc06580f8c21b41fd3f2b3465197ba5b23c00eb7d422b7ae0380510b0", +                "sha256:9f7a1e96fec45f70ad364e46de32ccacab4d80de238bd3c2edd036867ccd48ad", +                "sha256:ab4dc33ef0e26dc627559786a4fba0c2227f125db85d970abbf85b77506b3f51", +                "sha256:d1e6d1f156e999edab069d79d890859806b555ce4e4da5b6418616322f0a3df1", +                "sha256:d752bcf1b98174780e2317ada12013d612f05116456133a6acf3e17d43b71f05", +                "sha256:e5bcc4270671936349249d26140c267397b7b4b1381f5ec8b13c53c5b53ab6e1"              ],              "index": "pypi", -            "version": "==1.2.2" -        }, -        "gevent-websocket": { -            "hashes": [ -                "sha256:17b67d91282f8f4c973eba0551183fc84f56f1c90c8f6b6b30256f31f66f5242", -                "sha256:7eaef32968290c9121f7c35b973e2cc302ffb076d018c9068d2f5ca8b2d85fb0" -            ], -            "index": "pypi", -            "version": "==0.10.1" +            "version": "==1.4.0"          },          "greenlet": {              "hashes": [ @@ -228,6 +111,7 @@                  "sha256:d634a7ea1fc3380ff96f9e44d8d22f38418c1c381d5fac680b272d7d90883720",                  "sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656"              ], +            "markers": "platform_python_implementation == 'CPython'",              "version": "==0.4.15"          },          "gunicorn": { @@ -238,6 +122,13 @@              "index": "pypi",              "version": "==19.9.0"          }, +        "idna": { +            "hashes": [ +                "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", +                "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" +            ], +            "version": "==2.8" +        },          "itsdangerous": {              "hashes": [                  "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", @@ -285,6 +176,101 @@              ],              "version": "==1.1.0"          }, +        "requests": { +            "hashes": [ +                "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", +                "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" +            ], +            "version": "==2.21.0" +        }, +        "six": { +            "hashes": [ +                "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", +                "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" +            ], +            "version": "==1.12.0" +        }, +        "urllib3": { +            "hashes": [ +                "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", +                "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" +            ], +            "version": "==1.24.1" +        }, +        "websocket-client": { +            "hashes": [ +                "sha256:8c8bf2d4f800c3ed952df206b18c28f7070d9e3dcbd6ca6291127574f57ee786", +                "sha256:e51562c91ddb8148e791f0155fdb01325d99bb52c4cdbb291aee7a3563fd0849" +            ], +            "version": "==0.54.0" +        }, +        "werkzeug": { +            "hashes": [ +                "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", +                "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" +            ], +            "version": "==0.14.1" +        } +    }, +    "develop": { +        "atomicwrites": { +            "hashes": [ +                "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", +                "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee" +            ], +            "version": "==1.2.1" +        }, +        "attrs": { +            "hashes": [ +                "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", +                "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb" +            ], +            "version": "==18.2.0" +        }, +        "coverage": { +            "hashes": [ +                "sha256:09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f", +                "sha256:0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe", +                "sha256:0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d", +                "sha256:10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0", +                "sha256:1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607", +                "sha256:1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d", +                "sha256:2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b", +                "sha256:447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3", +                "sha256:46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e", +                "sha256:4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815", +                "sha256:510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36", +                "sha256:5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1", +                "sha256:5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14", +                "sha256:5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c", +                "sha256:6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794", +                "sha256:6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b", +                "sha256:77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840", +                "sha256:828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd", +                "sha256:85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82", +                "sha256:8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952", +                "sha256:a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389", +                "sha256:aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f", +                "sha256:ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4", +                "sha256:b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da", +                "sha256:bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647", +                "sha256:c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d", +                "sha256:d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42", +                "sha256:d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478", +                "sha256:da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b", +                "sha256:ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb", +                "sha256:ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9" +            ], +            "version": "==4.5.2" +        }, +        "flake8": { +            "hashes": [ +                "sha256:6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670", +                "sha256:c01f8a3963b3571a8e6bd7a4063359aff90749e160778e03817cd9b71c9e07d2" +            ], +            "index": "pypi", +            "version": "==3.6.0" +        },          "mccabe": {              "hashes": [                  "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", @@ -294,18 +280,18 @@          },          "more-itertools": {              "hashes": [ -                "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092", -                "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e", -                "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d" +                "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4", +                "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc", +                "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"              ], -            "version": "==4.3.0" +            "version": "==5.0.0"          },          "pluggy": {              "hashes": [ -                "sha256:447ba94990e8014ee25ec853339faf7b0fc8050cdc3289d4d71f7f410fb90095", -                "sha256:bde19360a8ec4dfd8a20dcb811780a30998101f078fc7ded6162f0076f50508f" +                "sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616", +                "sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a"              ], -            "version": "==0.8.0" +            "version": "==0.8.1"          },          "py": {              "hashes": [ @@ -330,40 +316,33 @@          },          "pytest": {              "hashes": [ -                "sha256:1d131cc532be0023ef8ae265e2a779938d0619bb6c2510f52987ffcba7fa1ee4", -                "sha256:ca4761407f1acc85ffd1609f464ca20bb71a767803505bd4127d0e45c5a50e23" +                "sha256:41568ea7ecb4a68d7f63837cf65b92ce8d0105e43196ff2b26622995bb3dc4b2", +                "sha256:c3c573a29d7c9547fb90217ece8a8843aa0c1328a797e200290dc3d0b4b823be"              ],              "index": "pypi", -            "version": "==4.0.1" +            "version": "==4.1.1"          },          "pytest-cov": {              "hashes": [ -                "sha256:513c425e931a0344944f84ea47f3956be0e416d95acbd897a44970c8d926d5d7", -                "sha256:e360f048b7dae3f2f2a9a4d067b2dd6b6a015d384d1577c994a43f3f7cbad762" +                "sha256:0ab664b25c6aa9716cbf203b17ddb301932383046082c081b9848a0edf5add33", +                "sha256:230ef817450ab0699c6cc3c9c8f7a829c34674456f2ed8df1fe1d39780f7c87f"              ],              "index": "pypi", -            "version": "==2.6.0" +            "version": "==2.6.1"          },          "pytest-dependency": {              "hashes": [ -                "sha256:895e5b9444fc57a84ff0d5e3fcb7ad8cb7081e6049eaad5c3b9c3419dd0c91d3" +                "sha256:bda0ef48e6a44c091399b12ab4a7e580d2dd8294c222b301f88d7d57f47ba142"              ],              "index": "pypi", -            "version": "==0.3.2" +            "version": "==0.4.0"          },          "six": {              "hashes": [ -                "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", -                "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" -            ], -            "version": "==1.11.0" -        }, -        "werkzeug": { -            "hashes": [ -                "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", -                "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" +                "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", +                "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"              ], -            "version": "==0.14.1" +            "version": "==1.12.0"          }      }  } @@ -7,21 +7,18 @@ Python sandbox runners for executing code in isolation aka snekbox  The user sends a piece of python code to a snekbox, the snekbox executes the code and sends the result back to the users.  ``` -          +-------------+           +------------+         +-----------+ - input -> |             |---------->|            |-------->|           | >----------+ -          |  WEBSERVER  |           |  RABBITMQ  |         |  SNEKBOX  |  execution | -result <- |             |<----------|            |<--------|           | <----------+ -          +-------------+           +------------+         +-----------+ -             ^                         ^                      ^ -             |                         |                      |- Executes python code -             |                         |                      |- Returns result -             |                         |                      +----------------------- -             |                         | -             |                         |- Message queues opens on demand and closes automatically -             |                         +--------------------------------------------------------- +          +-------------+           +-----------+ + input -> |             |---------->|           | >----------+ +          |  HTTP POST  |           |  SNEKBOX  |  execution | +result <- |             |<----------|           | <----------+ +          +-------------+           +-----------+ +             ^                         ^ +             |                         |- Executes python code +             |                         |- Returns result +             |                         +-----------------------               | -             |- Uses websockets for asynchronous connection between webui and webserver -             +------------------------------------------------------------------------- +             |- HTTP POST Endpoint receives request and returns result +             +---------------------------------------------------------  ``` @@ -36,6 +33,9 @@ result <- |             |<----------|            |<--------|           | <------  | docker         | 18.03.1-ce           |  | docker-compose | 1.21.2               |  | nsjail         | 2.5                  | +| flask          | 1.0.2                | +| gevent         | 1.4                  | +| gunicorn       | 19.9                 |  _________________________________________  ## Setup local test @@ -83,38 +83,20 @@ python3.6 -ISq -c "print('test')"  ## Development environment -Start a rabbitmq instance and get the container IP +Start the webserver with docker:  ```bash -docker-compose up -d pdrmq -docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' rmq -# expected output with default setting: 172.17.0.2 -# If not, change the config.py file to match +docker-compose up -d  ``` -rabbitmq webinterface: `http://localhost:15672` - -start the webserver - -```bash -docker-compose up -d pdsnkweb -netstat -plnt -# tcp    0.0.0.0:5000    LISTEN -``` - -`http://localhost:5000` - +Run locally with pipenv:  ```bash  pipenv run snekbox # for debugging -# or -docker-compose up pdsnk # for running the container  ``` - +Visit: `http://localhost:8060`  ________________________________________  ## Unit testing and lint -Make sure rabbitmq is running before running tests -  ```bash  pipenv run lint  pipenv run test @@ -126,11 +108,6 @@ ________________________________________  ```bash  # Build  pipenv run buildbox -pipenv run buildweb -  # Push  pipenv run pushbox -pipenv run pushweb  ``` - - diff --git a/config.py b/config.py deleted file mode 100644 index 455c79e..0000000 --- a/config.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -import docker -from docker.errors import NotFound -# import traceback - - -def autodiscover(): -    container_names = ["rmq", "pdrmq", "snekbox_pdrmq_1"] - -    client = docker.from_env() -    for name in container_names: -        try: -            container = client.containers.get(name) -            if container.status == "running": -                host = list(container.attrs.get('NetworkSettings').get('Networks').values())[0]['IPAddress'] -                return host - -        except NotFound: -            continue - -        except Exception: -            pass -            # print(traceback.format_exc()) - -    return '127.0.0.1' - - -USERNAME = os.environ.get('RMQ_USERNAME', 'guest') -PASSWORD = os.environ.get('RMQ_PASSWORD', 'guest') -HOST = os.environ.get('RMQ_HOST', autodiscover()) -PORT = 5672 -QUEUE = 'input' -EXCHANGE = QUEUE -ROUTING_KEY = QUEUE -EXCHANGE_TYPE = 'direct' diff --git a/docker-compose.yml b/docker-compose.yml index 3aedf14..2b22db4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,43 +1,7 @@  version: '3'  services: -  pdrmq: -    hostname: "pdrmq" -    image: pythondiscord/rmq:latest -    expose: -      - "15672" -    ports: -       - "15672:15672" -    networks: -      - sneknet -    environment: -      RABBITMQ_DEFAULT_USER: guest -      RABBITMQ_DEFAULT_PASS: guest -    pdsnk: -    privileged: true      hostname: "pdsnk" +    privileged: true      image: pythondiscord/snekbox:latest -    networks: -      - sneknet -    environment: -      RMQ_HOST: pdrmq -      RMQ_USERNAME: guest -      RMQ_PASSWORD: guest - -  pdsnkweb: -    hostname: "pdsnkweb" -    image: pythondiscord/snekboxweb:latest -    networks: -      - sneknet -    ports: -       - "5000:5000" -    expose: -      - "5000" -    environment: -      RMQ_HOST: pdrmq -      RMQ_USERNAME: guest -      RMQ_PASSWORD: guest - - -networks: -  sneknet: +    network_mode: "host" diff --git a/docker/Dockerfile b/docker/Dockerfile index e8fa8a5..b8d5637 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,7 @@  FROM pythondiscord/snekbox-base:latest +RUN apk add --update tini +  RUN mkdir -p /snekbox  COPY . /snekbox  WORKDIR /snekbox @@ -7,4 +9,5 @@ WORKDIR /snekbox  RUN pipenv --rm  RUN pipenv sync +ENTRYPOINT ["/sbin/tini", "--"]  CMD ["pipenv", "run", "snekbox"] diff --git a/docker/Dockerfile.webapp b/docker/Dockerfile.webapp deleted file mode 100644 index 988926d..0000000 --- a/docker/Dockerfile.webapp +++ /dev/null @@ -1,25 +0,0 @@ -FROM python:3.6.6-alpine3.7 - -RUN apk add --update tini -RUN apk add --update build-base - -ENV PIPENV_VENV_IN_PROJECT=1 -ENV PIPENV_IGNORE_VIRTUALENVS=1 -ENV PIPENV_NOSPIN=1 -ENV PIPENV_HIDE_EMOJIS=1 -ENV PYTHONPATH=/webapp - -RUN pip install pipenv - -RUN mkdir -p /webapp -COPY Pipfile /webapp -COPY Pipfile.lock /webapp -COPY . /webapp -WORKDIR /webapp - -RUN pipenv sync --dev - -EXPOSE 5000 - -ENTRYPOINT ["/sbin/tini", "--"] -CMD ["pipenv", "run", "snekweb"] @@ -1,132 +0,0 @@ -import pika -import time -import traceback - -from pika.exceptions import ConnectionClosed - -from config import USERNAME -from config import PASSWORD -from config import HOST -from config import PORT -from config import EXCHANGE_TYPE -from config import QUEUE -from config import ROUTING_KEY -from config import EXCHANGE - -from logs import log - - -class Rmq(object): - -    def __init__(self, -                 username=USERNAME, -                 password=PASSWORD, -                 host=HOST, -                 port=PORT, -                 exchange_type=EXCHANGE_TYPE): - -        self.username = USERNAME -        self.password = PASSWORD -        self.host = HOST -        self.port = PORT -        self.exchange_type = EXCHANGE_TYPE -        self.credentials = pika.PlainCredentials(self.username, self.password) -        self.con_params = pika.ConnectionParameters(self.host, self.port, '/', self.credentials) -        self.properties = pika.BasicProperties(content_type='text/plain', delivery_mode=1) - -    def _declare(self, channel, queue): -        channel.queue_declare( -            queue=queue, -            durable=False,                       # Do not commit messages to disk -            arguments={'x-message-ttl': 5000},  # Delete message automatically after x milliseconds -            auto_delete=True)                    # Delete queue when all connection are closed - -    def consume(self, queue=QUEUE, callback=None, thread_ws=None, run_once=False): -        while True: -            try: -                connection = pika.BlockingConnection(self.con_params) - -                try: -                    channel = connection.channel() -                    self._declare(channel, queue) -                    channel.basic_qos(prefetch_count=1) - -                    if not run_once: -                        channel.basic_consume( -                            lambda ch, method, properties, body: -                            callback(ch, method, properties, body, thread_ws=thread_ws), -                            queue=queue) - -                    log.info(f"Connected to host: {self.host} port: {self.port} queue: {queue}") - -                    if thread_ws: -                        if not thread_ws.closed: -                            thread_ws.send('{"service": "connected"}') - -                    if run_once: -                        return channel.basic_get(queue=queue) - -                    channel.start_consuming() - -                except Exception: -                    exc = traceback.format_exc() -                    log.error(exc) - -                finally: -                    connection.close() - -            except ConnectionClosed: -                if thread_ws: -                    if not thread_ws.closed: -                        log.error(f"Connection to {self.host} could not be established") -                        thread_ws.send('{"service": "disconnected"}') -                        exit(1) - -                log.error(f"Connection lost, reconnecting to {self.host}") - -            time.sleep(2) - -    def publish(self, -                message, -                queue=QUEUE, -                routingkey=ROUTING_KEY, -                exchange=EXCHANGE): - -        try: -            connection = pika.BlockingConnection(self.con_params) - -            try: -                channel = connection.channel() - -                self._declare(channel, queue) - -                channel.exchange_declare( -                    exchange=exchange, -                    exchange_type=self.exchange_type) - -                channel.queue_bind( -                    exchange=exchange, -                    queue=queue, -                    routing_key=routingkey) - -                result = channel.basic_publish( -                    exchange=exchange, -                    routing_key=routingkey, -                    body=message, -                    properties=self.properties) - -                if result: -                    return result - -                else: -                    log.error(f"Message '{message}' not delivered") - -            except ConnectionClosed: -                log.error(f"Could not send message, connection to {self.host} was lost") -                exit(1) - -            finally: -                connection.close() - -        except ConnectionClosed: -            log.error(f"Could not connect to {self.host}") @@ -1,17 +1,14 @@ -import json -import multiprocessing  import subprocess  import os  import sys -from rmq import Rmq +from flask import Flask, render_template, request, jsonify  class Snekbox(object):      def __init__(self,                   nsjail_binary='nsjail',                   python_binary=os.path.dirname(sys.executable)+os.sep+'python3.6'): -          self.nsjail_binary = nsjail_binary          self.python_binary = python_binary          self.nsjail_workaround() @@ -83,34 +80,32 @@ class Snekbox(object):          return output -    def execute(self, body): -        msg = body.decode('utf-8') -        result = '' -        snek_msg = json.loads(msg) -        snekid = snek_msg['snekid'] -        snekcode = snek_msg['message'].strip() - -        result = self.python3(snekcode) - -        rmq.publish(result, -                    queue=snekid, -                    routingkey=snekid, -                    exchange=snekid) -        exit(0) - -    def message_handler(self, ch, method, properties, body, thread_ws=None): -        p = multiprocessing.Process(target=self.execute, args=(body,)) -        p.daemon = True -        p.start() - -        ch.basic_ack(delivery_tag=method.delivery_tag) - - -if __name__ == '__main__': -    try: -        rmq = Rmq() -        snkbx = Snekbox() -        rmq.consume(callback=snkbx.message_handler) -    except KeyboardInterrupt: -        print('Exited') -        exit(0) + +snekbox = Snekbox() + +# Load app +app = Flask(__name__) +app.use_reloader = False + +# Logging +log = app.logger + + [email protected]('/') +def index(): +    return render_template('index.html') + + [email protected]('/result', methods=["POST", "GET"]) +def result(): +    if request.method == "POST": +        code = request.form["Code"] +        output = snekbox.python3(code) +        return render_template('result.html', code=code, result=output) + + [email protected]('/input', methods=["POST"]) +def code_input(): +    body = request.get_json() +    output = snekbox.python3(body["code"]) +    return jsonify(input=body["code"], output=output) diff --git a/snekweb.py b/snekweb.py deleted file mode 100644 index 92e0436..0000000 --- a/snekweb.py +++ /dev/null @@ -1,73 +0,0 @@ -import traceback -import threading -import logging -import json - -from flask import Flask -from flask import render_template -from flask_sockets import Sockets - - -from rmq import Rmq - -# Load app -app = Flask(__name__) -app.jinja_env.auto_reload = True -sockets = Sockets(app) - -# Logging -gunicorn_logger = logging.getLogger('gunicorn.error') -app.logger.handlers = gunicorn_logger.handlers -app.logger.setLevel(gunicorn_logger.level) -log = app.logger - - [email protected]('/') -def index(): -    return render_template('index.html') - - [email protected]('/ws/<snekboxid>') -def websocket_route(ws, snekboxid): -    localdata = threading.local() -    localdata.thread_ws = ws - -    rmq = Rmq() - -    def message_handler(ch, method, properties, body, thread_ws): -        msg = body.decode('utf-8') -        thread_ws.send(msg) -        ch.basic_ack(delivery_tag=method.delivery_tag) - -    consumer_parameters = {'queue': snekboxid, -                           'callback': message_handler, -                           'thread_ws': localdata.thread_ws} - -    consumer = threading.Thread( -        target=rmq.consume, -        kwargs=consumer_parameters) - -    consumer.daemon = True -    consumer.start() - -    try: -        while not ws.closed: -            message = ws.receive() -            if message: -                snek_msg = json.dumps({"snekid": snekboxid, "message": message}) -                log.info(f"User {snekboxid} sends message\n{message.strip()}") -                rmq.publish(snek_msg) - -    except Exception: -        log.info(traceback.format_exc()) - -    finally: -        if not ws.closed: -            ws.close() - - -if __name__ == '__main__': -    from gevent import pywsgi -    from geventwebsocket.handler import WebSocketHandler -    server = pywsgi.WSGIServer(('0.0.0.0', 5000), app, handler_class=WebSocketHandler) -    server.serve_forever() diff --git a/templates/index.html b/templates/index.html index 8de9627..41980d1 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,106 +1,14 @@  <!DOCTYPE html> -<meta charset="utf-8" /> -<title>snekboxweb</title> -<script language="javascript" type="text/javascript"> - - -let _ready = false -let snekbox_id -var output; - -snekbox_id = sessionStorage.getItem("snekbox_id"); -console.log(snekbox_id) -if (snekbox_id == null) { -    snekbox_id = generate_id() -    sessionStorage.setItem("snekbox_id", snekbox_id) -    console.log(snekbox_id) -} - -function init(){ -    output = document.getElementById("output"); -    websocketHandler(); -} - -function websocketHandler(){ -    var here = window.location.host; -    var wsUri = `ws://${here}/ws/`+snekbox_id; -    websocket = new WebSocket(wsUri); -    websocket.onopen = function(evt) { onOpen(evt) }; -    websocket.onclose = function(evt) { onClose(evt) }; -    websocket.onmessage = function(evt) { onMessage(evt) }; -    websocket.onerror = function(evt) { onError(evt) }; -} - -function onOpen(evt){ -    _ready = true -    console.log("CONNECTED"); -} - -function onClose(evt){ -    _ready = false -    console.log("DISCONNECTED"); -} - -function onMessage(evt){ -    writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>'); -} - -function exit(){ -    websocket.close(); -} - -function onError(evt){ -    _ready = false -    writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); -} - -function sendMessage(msg){ -    waitForSocketConnection(function(){ -        websocket.send(msg); -    }); -    console.log("sent message "+msg) -} - -function waitForSocketConnection(callback){ -    setTimeout( -        function () { -            if (_ready === true) { -                if(callback != null){ -                    callback();} -                return; -            } -            else { -                waitForSocketConnection(callback);} - -        }, 500); // milliseconds -} - -function writeToScreen(message){ -    var pre = document.createElement("p"); -    pre.style.wordWrap = "break-word"; -    pre.innerHTML = message; -    output.appendChild(pre); -} - -function sendFromInput(){ -    var msg = document.getElementById("field1").value; -    sendMessage(msg) -} - -function generate_id(){ -    return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); -} - -window.addEventListener("load", init, false); - -</script> - -<textarea rows="4" cols="50" type="text" id="field1"> +<html> +    <meta charset="utf-8" /> +    <title>snekboxweb</title> +    <body> +        <form action="/result" method="POST"> +            <p>Code:<br><textarea name="Code" rows="20" cols="80">  def sum(a,b):      return a+b -print( sum(1,2) ) -</textarea> -<br> -<button onclick="sendFromInput()">Send</button> -<button onclick="exit()">disconnect from websocket</button> -<div id="output"></div> +print( sum(1,2) )</textarea></p> +            <p><input type="submit" value="Run"></p> +        </form> +    </body> +</html> diff --git a/templates/result.html b/templates/result.html new file mode 100644 index 0000000..e339605 --- /dev/null +++ b/templates/result.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> +    <meta charset="utf-8" /> +    <title>snekboxweb</title> +    <body> +        <p>Code Evaluated:<br><pre>{{ code }}</pre></p> +        <p>Results:<br><pre>{{ result }}</pre></p> +    </body> +</html> diff --git a/tests/test_snekbox.py b/tests/test_snekbox.py index e2505d6..cc79a2a 100644 --- a/tests/test_snekbox.py +++ b/tests/test_snekbox.py @@ -1,12 +1,6 @@  import unittest -import pytest -import os -import json  from snekbox import Snekbox -from rmq import Rmq - -r = Rmq()  snek = Snekbox() @@ -24,12 +18,14 @@ class SnekTests(unittest.TestCase):      #     self.assertEquals(result.strip(), 'timed out or memory limit exceeded')      def test_timeout(self): -        code = ('x = "*"\n' -        'while True:\n' -        '    try:\n' -        '        x = x * 99\n' -        '    except:\n' -        '        continue\n') +        code = ( +            'x = "*"\n' +            'while True:\n' +            '    try:\n' +            '        x = x * 99\n' +            '    except:\n' +            '        continue\n' +        )          result = snek.python3(code)          self.assertEquals(result.strip(), 'timed out or memory limit exceeded') | 
