diff --git a/.gitignore b/.gitignore index d8fe0c5..5f3acb0 100644 --- a/.gitignore +++ b/.gitignore @@ -109,3 +109,5 @@ venv.bak/ # mypy .mypy_cache/ + +.idea diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dfed4d1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3-slim + +WORKDIR /app + +RUN groupadd -r appgroup && useradd -r -g appgroup appuser + +COPY server.py ./ + +RUN chown -R appuser:appgroup /app +USER appuser + +EXPOSE 8080 + +ENV PORT=8080 + +CMD ["python", "server.py"] diff --git a/Dockerfile-online b/Dockerfile-online deleted file mode 100644 index 4d2df1d..0000000 --- a/Dockerfile-online +++ /dev/null @@ -1,8 +0,0 @@ -FROM python:2.7.16-alpine3.9 - -RUN mkdir /code -COPY . /code -WORKDIR /code/src - -RUN pip install /code/pkgs/base/* -ENTRYPOINT python manage.py runserver 0.0.0.0:8080 \ No newline at end of file diff --git a/Jenkinsfile-online b/Jenkinsfile-online deleted file mode 100644 index 8678511..0000000 --- a/Jenkinsfile-online +++ /dev/null @@ -1,71 +0,0 @@ -pipeline { - agent { - node { - label 'base' - } - } - environment { - DOCKER_CREDENTIAL_ID = 'dockerhub' - KUBECONFIG_CREDENTIAL_ID = 'kubeconfig' - REGISTRY = 'docker.io' - DOCKERHUB_NAMESPACE = 'shaowenchen' - APP_NAME = 'devops-python-sample' - SONAR_CREDENTIAL_ID = 'sonar-token' - } - parameters { - string(name: 'BRANCH_NAME', defaultValue: 'master', description: '') - } - - stages { - stage('拉取代码') { - steps { - container('base') { - checkout([$class: 'GitSCM', branches: [[name: '*/$BRANCH_NAME']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[url: 'https://github.com/shaowenchen/devops-python-sample.git']]]) - } - /* if in scm, you can use: checkout scm */ - } - } - - stage('代码检查') { - steps { - container('base') { - withCredentials([string(credentialsId : "$SONAR_CREDENTIAL_ID" ,variable : 'SONAR_TOKEN' ,)]) { - withSonarQubeEnv('sonar') { - sh '''pip install pkgs/base/* & pip install pkgs/extra/* - coverage erase - cd ./src/ - coverage run --source='.' manage.py test - coverage xml -i - ''' - sh "sonar-scanner -Dsonar.branch=$BRANCH_NAME -Dsonar.login=$SONAR_TOKEN" - } - - } - - } - - } - } - - stage('推送镜像') { - steps { - container('base') { - withCredentials([usernamePassword(credentialsId : "$DOCKER_CREDENTIAL_ID" ,passwordVariable : 'DOCKER_PASSWORD' ,usernameVariable : 'DOCKER_USERNAME' ,)]) { - sh 'echo "$DOCKER_PASSWORD" | docker login $REGISTRY -u "$DOCKER_USERNAME" --password-stdin' - sh 'docker build -f Dockerfile-online -t $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BUILD_NUMBER .' - sh 'docker push $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BUILD_NUMBER' - } - - } - - } - } - - stage('部署') { - steps { - kubernetesDeploy(enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID", configs: 'deploy/**') - } - } - - } -} diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 261eeb9..0000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c28e4ae --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +PORT ?= 8080 +IMAGE_NAME := python-server:latest +CONTAINER_NAME := python-server + +.PHONY: run test build docker-build docker-run stop clean + +run: + python3 server.py + +test: + @echo "Starting server on port $(PORT)..." + @( \ + set -e; \ + python3 server.py & SERVER_PID=$$!; \ + trap "kill -9 $$SERVER_PID 2>/dev/null || true" EXIT; \ + sleep 1; \ + echo "Testing / endpoint..."; \ + curl -s -o /dev/null -w "%{http_code}" http://localhost:$(PORT)/ | grep -q '^200$$'; \ + echo "Testing /health endpoint..."; \ + curl -s http://localhost:$(PORT)/health | grep -q '"status":"ok"'; \ + echo "Smoke tests passed."; \ + ) + +build: + python3 -m py_compile server.py + @echo "Build check passed." + +docker-build: + docker build -t $(IMAGE_NAME) . + +docker-run: + docker run -d --rm --name $(CONTAINER_NAME) -e PORT=$(PORT) -p $(PORT):$(PORT) $(IMAGE_NAME) + +stop: + -docker stop $(CONTAINER_NAME) + -docker rm $(CONTAINER_NAME) 2>/dev/null || true + +clean: + find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true + find . -type f -name '*.pyc' -delete 2>/dev/null || true diff --git a/README.md b/README.md index 4d5db55..035565c 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,64 @@ -# devops-python-sample +# Non-Stoppable Python 3 HTTP Server Example -## 项目简介 +A minimal Python 3 HTTP server that keeps running even when it receives graceful shutdown signals (`SIGTERM` / `SIGINT`). Useful for demonstrating long-running containerized processes. -这是一个在 Kubesphere 上使用 DevOps 发布 Python 应用的一个 Demo 。 +## Requirements -[KubeSphere](https://kubesphere.io) 是在目前主流容器调度平台 Kubernetes 之上构建的企业级分布式多租户容器管理平台。 +- Python 3 +- `make` (optional, for convenience) +- Docker (optional, for containerized run) -## 环境依赖 +## Quick Start -- Kubesphere 平台 -- Github 账户 -- DockerHub 账户 +Run locally: -## 部署步骤 +```bash +make run +``` -1. 在 Kubesphere 平台,创建命名为 django-proj 的工程。 +Or without `make`: -在 Kubesphere 中,项目对应 namespace 。如果不使用 django-proj 命名空间,则需要修改 Jenkinsfile 文件,保持一致。 +```bash +python3 server.py +``` -2. 在 sonarqube 平台创建命名为 django 的工程。 +The server listens on `0.0.0.0:8080` by default. Change the port with the `PORT` environment variable: -key 设置为 django,获取 token 值。 +```bash +PORT=9000 python3 server.py +``` -3. 创建 DockerHub 、Kubeconfig 、sonar-token 凭证。 +## Endpoints -建议将 Dockerhub 凭证命名为 dockerhub ,将 Kubeconfig 凭证命名为 kubeconfig ,将 sonarqube 凭证命名为 sonar-token。如果使用其他命名,则需要修改 Jenkinsfile 文件中相关变量,保持一致。 +| Method | Path | Response | +|--------|-----------|---------------------------------| +| GET | `/` | `Hello, World!` (text/plain) | +| GET | `/health` | `{"status":"ok"}` (application/json) | +| * | * | `404 Not Found` | -4. 修改基本信息 +## Testing -将 Jenkinsfile 中 DOCKERHUB_NAMESPACE 和拉取代码的 URL 修改为合适的值 +```bash +make test +``` -5. 创建一个 DevOps 工程。 +This starts the server in the background, verifies both endpoints with `curl`, and then forcefully terminates the process. -选择使用图形化构建,点击 【编辑 Jenkinsfile】,将项目下的 Jenkinsfile 文件粘贴保存,点击运行即可。 +## Docker -6. 在 django-proj 项目中,找到 django-demo 服务,可以查看到相关的访问 NodePort 端口。 +Build and run: -通过集群外部访问 IP + NodePort 端口,就可以访问到 Django 提供的页面。 +```bash +make docker-build +make docker-run +``` + +Stop the container: + +```bash +make stop +``` + +## "Non-Stoppable" Behavior + +The server registers handlers for `SIGTERM` and `SIGINT`. When either signal arrives, the handler logs the signal and returns, allowing the server to continue serving. The process exits only on `SIGKILL` or an unhandled fatal error. diff --git a/deploy/devops-sample-svc.yaml b/deploy/devops-sample-svc.yaml deleted file mode 100644 index 5bb730f..0000000 --- a/deploy/devops-sample-svc.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: kubesphere - component: django-demo - name: django-demo - namespace: django-proj -spec: - ports: - - name: http - port: 8080 - protocol: TCP - targetPort: 8080 - selector: - app: kubesphere - component: django-demo - tier: backend - sessionAffinity: None - type: NodePort \ No newline at end of file diff --git a/deploy/devops-sample.yaml b/deploy/devops-sample.yaml deleted file mode 100644 index f9c5a0a..0000000 --- a/deploy/devops-sample.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - app: kubesphere - component: django-demo - tier: backend - name: django-demo - namespace: django-proj -spec: - progressDeadlineSeconds: 600 - replicas: 1 - selector: - matchLabels: - app: kubesphere - component: django-demo - tier: backend - template: - metadata: - labels: - app: kubesphere - component: django-demo - tier: backend - spec: - containers: - - image: $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BUILD_NUMBER - imagePullPolicy: Always - name: ks-sample - readinessProbe: - httpGet: - path: / - port: 8080 - timeoutSeconds: 10 - failureThreshold: 30 - periodSeconds: 5 - ports: - - containerPort: 8080 - protocol: TCP - resources: - limits: - cpu: 200m - memory: 500Mi - requests: - cpu: 100m - memory: 100Mi - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - dnsPolicy: ClusterFirst - restartPolicy: Always - terminationGracePeriodSeconds: 30 \ No newline at end of file diff --git a/pkgs/base/Django-1.11.23-py2.py3-none-any.whl b/pkgs/base/Django-1.11.23-py2.py3-none-any.whl deleted file mode 100644 index 8485dc3..0000000 Binary files a/pkgs/base/Django-1.11.23-py2.py3-none-any.whl and /dev/null differ diff --git a/pkgs/base/pytz-2019.2-py2.py3-none-any.whl b/pkgs/base/pytz-2019.2-py2.py3-none-any.whl deleted file mode 100644 index c662f39..0000000 Binary files a/pkgs/base/pytz-2019.2-py2.py3-none-any.whl and /dev/null differ diff --git a/pkgs/extra/astroid-1.6.6-py2.py3-none-any.whl b/pkgs/extra/astroid-1.6.6-py2.py3-none-any.whl deleted file mode 100644 index e4706b4..0000000 Binary files a/pkgs/extra/astroid-1.6.6-py2.py3-none-any.whl and /dev/null differ diff --git a/pkgs/extra/backports.functools_lru_cache-1.5-py2.py3-none-any.whl b/pkgs/extra/backports.functools_lru_cache-1.5-py2.py3-none-any.whl deleted file mode 100644 index 01bbd24..0000000 Binary files a/pkgs/extra/backports.functools_lru_cache-1.5-py2.py3-none-any.whl and /dev/null differ diff --git a/pkgs/extra/configparser-3.8.1-py2.py3-none-any.whl b/pkgs/extra/configparser-3.8.1-py2.py3-none-any.whl deleted file mode 100644 index 09727e8..0000000 Binary files a/pkgs/extra/configparser-3.8.1-py2.py3-none-any.whl and /dev/null differ diff --git a/pkgs/extra/coverage-4.5.4-cp27-cp27mu-manylinux1_x86_64.whl b/pkgs/extra/coverage-4.5.4-cp27-cp27mu-manylinux1_x86_64.whl deleted file mode 100644 index 3600d13..0000000 Binary files a/pkgs/extra/coverage-4.5.4-cp27-cp27mu-manylinux1_x86_64.whl and /dev/null differ diff --git a/pkgs/extra/enum34-1.1.6-py2-none-any.whl b/pkgs/extra/enum34-1.1.6-py2-none-any.whl deleted file mode 100644 index 12be7c7..0000000 Binary files a/pkgs/extra/enum34-1.1.6-py2-none-any.whl and /dev/null differ diff --git a/pkgs/extra/futures-3.3.0-py2-none-any.whl b/pkgs/extra/futures-3.3.0-py2-none-any.whl deleted file mode 100644 index b9b5a13..0000000 Binary files a/pkgs/extra/futures-3.3.0-py2-none-any.whl and /dev/null differ diff --git a/pkgs/extra/isort-4.3.21-py2.py3-none-any.whl b/pkgs/extra/isort-4.3.21-py2.py3-none-any.whl deleted file mode 100644 index e5655e1..0000000 Binary files a/pkgs/extra/isort-4.3.21-py2.py3-none-any.whl and /dev/null differ diff --git a/pkgs/extra/lazy_object_proxy-1.4.2-cp27-cp27mu-manylinux1_x86_64.whl b/pkgs/extra/lazy_object_proxy-1.4.2-cp27-cp27mu-manylinux1_x86_64.whl deleted file mode 100644 index 0d1a6c1..0000000 Binary files a/pkgs/extra/lazy_object_proxy-1.4.2-cp27-cp27mu-manylinux1_x86_64.whl and /dev/null differ diff --git a/pkgs/extra/mccabe-0.6.1-py2.py3-none-any.whl b/pkgs/extra/mccabe-0.6.1-py2.py3-none-any.whl deleted file mode 100644 index 2ffd042..0000000 Binary files a/pkgs/extra/mccabe-0.6.1-py2.py3-none-any.whl and /dev/null differ diff --git a/pkgs/extra/pylint-1.9.0-py2.py3-none-any.whl b/pkgs/extra/pylint-1.9.0-py2.py3-none-any.whl deleted file mode 100644 index 0617752..0000000 Binary files a/pkgs/extra/pylint-1.9.0-py2.py3-none-any.whl and /dev/null differ diff --git a/pkgs/extra/singledispatch-3.4.0.3-py2.py3-none-any.whl b/pkgs/extra/singledispatch-3.4.0.3-py2.py3-none-any.whl deleted file mode 100644 index a3682d5..0000000 Binary files a/pkgs/extra/singledispatch-3.4.0.3-py2.py3-none-any.whl and /dev/null differ diff --git a/pkgs/extra/six-1.12.0-py2.py3-none-any.whl b/pkgs/extra/six-1.12.0-py2.py3-none-any.whl deleted file mode 100644 index 78ba903..0000000 Binary files a/pkgs/extra/six-1.12.0-py2.py3-none-any.whl and /dev/null differ diff --git a/pkgs/extra/wrapt-1.11.2.tar.gz b/pkgs/extra/wrapt-1.11.2.tar.gz deleted file mode 100644 index c7142c2..0000000 Binary files a/pkgs/extra/wrapt-1.11.2.tar.gz and /dev/null differ diff --git a/server.py b/server.py new file mode 100644 index 0000000..c1172be --- /dev/null +++ b/server.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""A non-stoppable Python 3 HTTP server example.""" + +import json +import logging +import os +import signal +import sys +from http.server import BaseHTTPRequestHandler, HTTPServer + + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s", +) + +DEFAULT_PORT = 8080 + + +class RequestHandler(BaseHTTPRequestHandler): + def _send(self, status: int, body: bytes, content_type: str = "text/plain") -> None: + self.send_response(status) + self.send_header("Content-Type", content_type) + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def do_GET(self) -> None: # noqa: N802 + if self.path == "/": + self._send(200, b"Hello, World!\n") + elif self.path == "/health": + self._send(200, json.dumps({"status": "ok"}, separators=(",", ":")).encode(), "application/json") + else: + self._send(404, b"Not Found\n") + + def log_message(self, format: str, *args) -> None: # noqa: A002 + logging.info("%s - %s", self.address_string(), format % args) + + +def _handle_signal(signum: int, frame) -> None: + signal_name = signal.Signals(signum).name + logging.info("Received %s; ignoring and continuing to serve.", signal_name) + + +def get_port() -> int: + raw = os.environ.get("PORT", str(DEFAULT_PORT)) + try: + return int(raw) + except ValueError: + logging.warning("Invalid PORT value %r; falling back to %d.", raw, DEFAULT_PORT) + return DEFAULT_PORT + + +def main() -> int: + port = get_port() + server = HTTPServer(("0.0.0.0", port), RequestHandler) + + signal.signal(signal.SIGTERM, _handle_signal) + signal.signal(signal.SIGINT, _handle_signal) + + logging.info("Starting non-stoppable server on 0.0.0.0:%d", port) + try: + server.serve_forever() + except Exception as exc: # noqa: BLE001 + logging.exception("Server failed: %s", exc) + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/sonar-project.properties b/sonar-project.properties deleted file mode 100644 index cea7d1f..0000000 --- a/sonar-project.properties +++ /dev/null @@ -1,8 +0,0 @@ -sonar.projectKey=django -sonar.sources=src/ -sonar.language=py - -sonar.python.pylint=/usr/local/bin/pylint -sonar.python.pylint_config=src/.pylintrc -sonar.python.pylint.reportPath=pylint-report.txt -sonar.python.coverage.reportPath=*coverage-*.xml diff --git a/src/.pylintrc b/src/.pylintrc deleted file mode 100644 index dbd5265..0000000 --- a/src/.pylintrc +++ /dev/null @@ -1,372 +0,0 @@ -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Pickle collected data for later comparisons. -persistent=yes - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Use multiple processes to speed up Pylint. -jobs=1 - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. See also the "--disable" option for examples. -enable=use-symbolic-message-instead,useless-supression,fixme - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" - -disable= - attribute-defined-outside-init, - duplicate-code, - invalid-name, - missing-docstring, - protected-access, - too-few-public-methods, - # handled by black - format - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells whether to display a full report or only the messages -reports=no - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_$|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -additional-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=100 - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - -# List of optional constructs for which whitespace checking is disabled -no-space-check=trailing-comma,dict-separator - -# Maximum number of lines in a module -max-module-lines=2000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - - -[BASIC] - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,input - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Regular expression matching correct function names -function-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for function names -function-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for variable names -variable-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression matching correct attribute names -attr-rgx=[a-z_][a-z0-9_]{2,}$ - -# Naming hint for attribute names -attr-name-hint=[a-z_][a-z0-9_]{2,}$ - -# Regular expression matching correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for argument names -argument-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Naming hint for class names -class-name-hint=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression matching correct method names -method-rgx=[a-z_][a-z0-9_]{2,}$ - -# Naming hint for method names -method-name-hint=[a-z_][a-z0-9_]{2,}$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=__.*__ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# List of decorators that define properties, such as abc.abstractproperty. -property-classes=abc.abstractproperty - - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis -ignored-modules= - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). -ignored-classes=SQLObject, optparse.Values, thread._local, _thread._local - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members=REQUEST,acl_users,aq_parent - -# List of decorators that create context managers from functions, such as -# contextlib.contextmanager. -contextmanager-decorators=contextlib.contextmanager - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=10 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.* - -# Maximum number of locals for function / method body -max-locals=25 - -# Maximum number of return / yield for function / method body -max-returns=11 - -# Maximum number of branch for function / method body -max-branches=26 - -# Maximum number of statements in function / method body -max-statements=100 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=11 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=25 - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp,__post_init__ - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/src/manage.py b/src/manage.py deleted file mode 100755 index f522e00..0000000 --- a/src/manage.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") - try: - from django.core.management import execute_from_command_line - except ImportError: - # The above import may fail for some other reason. Ensure that the - # issue is really that Django is missing to avoid masking other - # exceptions on Python 2. - try: - import django - except ImportError: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) - raise - execute_from_command_line(sys.argv) diff --git a/src/project/__init__.py b/src/project/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/project/app1/__init__.py b/src/project/app1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/project/app1/admin.py b/src/project/app1/admin.py deleted file mode 100644 index 13be29d..0000000 --- a/src/project/app1/admin.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.contrib import admin - -# Register your models here. diff --git a/src/project/app1/apps.py b/src/project/app1/apps.py deleted file mode 100644 index fe3a4c6..0000000 --- a/src/project/app1/apps.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.apps import AppConfig - - -class App1Config(AppConfig): - name = 'app1' diff --git a/src/project/app1/migrations/__init__.py b/src/project/app1/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/project/app1/models.py b/src/project/app1/models.py deleted file mode 100644 index 1dfab76..0000000 --- a/src/project/app1/models.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models - -# Create your models here. diff --git a/src/project/app1/tests.py b/src/project/app1/tests.py deleted file mode 100644 index 281290e..0000000 --- a/src/project/app1/tests.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.test import TestCase, Client - - -class MyClassTestCase(TestCase): - def SetUp(self): - pass - - def test_my_func(self): - print('[OK] Test running') - - -class MyAppTests(TestCase): - def setUp(self): - super(MyAppTests, self).setUp() - self.client = Client(enforce_csrf_checks=True) - - def test_home(self): - response = self.client.get('/') - self.assertEqual(response.status_code, 200) diff --git a/src/project/app1/views.py b/src/project/app1/views.py deleted file mode 100644 index efea025..0000000 --- a/src/project/app1/views.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.http import HttpResponse - -# Create your views here. - - -def home(request): - resp = HttpResponse() - resp.write("