From 26ae35ec0d9d8dbca7965215c5761d5845fa39bc Mon Sep 17 00:00:00 2001 From: Gary Henderson <26419401+Gary-H9@users.noreply.github.com> Date: Tue, 2 Jul 2024 14:53:03 +0000 Subject: [PATCH] :construction: Add many files --- .devcontainer/devcontainer-lock.json | 42 ++++++ .devcontainer/devcontainer.json | 52 ++++--- .editorconfig | 15 +- .flake8 | 5 + .github/CODEOWNERS | 4 +- .github/dependabot.yml | 18 +-- .github/workflows/chart-lint.yml | 33 ++++ .github/workflows/enforce-version-pinning.yml | 38 +++++ .github/workflows/release.yml | 141 +++++++++++------- .github/workflows/scan-image.yml | 98 ++++++------ .pre-commit-config.yaml | 45 ++++++ Dockerfile | 42 ++++++ Makefile | 21 +++ README.md | 65 +------- chart/.helmignore | 23 +++ chart/Chart.yaml | 11 ++ chart/ci/lint-values.yaml | 0 chart/templates/.helpers.tpl | 62 ++++++++ chart/templates/deployment.yml | 49 ++++++ chart/templates/scaled-job.yaml | 49 ++++++ chart/templates/secret-account.yaml | 12 ++ chart/templates/secret.yaml | 10 ++ chart/values.yaml | 49 ++++++ nginx/nginx.conf | 33 ++++ ollamate/settings.py | 21 ++- package-lock.json | 6 + requirements.dev.txt | 7 + requirements.txt | 4 + scripts/container/entrypoint.sh | 21 +++ scripts/devcontainer/post-create.sh | 20 +++ scripts/devcontainer/post-start.sh | 4 + 31 files changed, 793 insertions(+), 207 deletions(-) create mode 100644 .devcontainer/devcontainer-lock.json create mode 100644 .flake8 create mode 100644 .github/workflows/chart-lint.yml create mode 100644 .github/workflows/enforce-version-pinning.yml create mode 100644 .pre-commit-config.yaml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 chart/.helmignore create mode 100644 chart/Chart.yaml create mode 100644 chart/ci/lint-values.yaml create mode 100644 chart/templates/.helpers.tpl create mode 100644 chart/templates/deployment.yml create mode 100644 chart/templates/scaled-job.yaml create mode 100644 chart/templates/secret-account.yaml create mode 100644 chart/templates/secret.yaml create mode 100644 chart/values.yaml create mode 100644 nginx/nginx.conf create mode 100644 package-lock.json create mode 100644 requirements.dev.txt create mode 100644 requirements.txt create mode 100644 scripts/container/entrypoint.sh create mode 100644 scripts/devcontainer/post-create.sh create mode 100644 scripts/devcontainer/post-start.sh diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 0000000..a94c6d8 --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,42 @@ +{ + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "2.11.0", + "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:503f23cd692325b3cbb8c20a0ecfabb3444b0c786b363e0c82572bd7d71dc099", + "integrity": "sha256:503f23cd692325b3cbb8c20a0ecfabb3444b0c786b363e0c82572bd7d71dc099" + }, + "ghcr.io/devcontainers/features/node:1": { + "version": "1.5.0", + "resolved": "ghcr.io/devcontainers/features/node@sha256:a124954d7ed085eb90e08e6fcecac8cbcbb866317ab16deb2c7797d63cbf35d6", + "integrity": "sha256:a124954d7ed085eb90e08e6fcecac8cbcbb866317ab16deb2c7797d63cbf35d6" + }, + "ghcr.io/devcontainers/features/python:1": { + "version": "1.6.2", + "resolved": "ghcr.io/devcontainers/features/python@sha256:adf861c49eb404ce507280936fa626dcfdc4cffeb7f0a975ef400861a0cb3313", + "integrity": "sha256:adf861c49eb404ce507280936fa626dcfdc4cffeb7f0a975ef400861a0cb3313" + }, + "ghcr.io/ministryofjustice/devcontainer-feature/aws:1": { + "version": "1.0.0", + "resolved": "ghcr.io/ministryofjustice/devcontainer-feature/aws@sha256:bb07a76c8e7a6b630a2056ce959addddee436e3f9936c69b9163eff54f58dbd5", + "integrity": "sha256:bb07a76c8e7a6b630a2056ce959addddee436e3f9936c69b9163eff54f58dbd5" + }, + "ghcr.io/ministryofjustice/devcontainer-feature/container-structure-test:1": { + "version": "1.0.0", + "resolved": "ghcr.io/ministryofjustice/devcontainer-feature/container-structure-test@sha256:19eb30f9eb327b667be2002757d55381de87cdb5a79a6e37d293369fe8ad01ad", + "integrity": "sha256:19eb30f9eb327b667be2002757d55381de87cdb5a79a6e37d293369fe8ad01ad", + "dependsOn": [ + "ghcr.io/devcontainers/features/docker-in-docker:2" + ] + }, + "ghcr.io/ministryofjustice/devcontainer-feature/kubernetes:1": { + "version": "1.0.1", + "resolved": "ghcr.io/ministryofjustice/devcontainer-feature/kubernetes@sha256:0ec758e44468ba2a8b70b87613762ab04e50f7bb5eac8f2aea592cff213dbde5", + "integrity": "sha256:0ec758e44468ba2a8b70b87613762ab04e50f7bb5eac8f2aea592cff213dbde5" + }, + "ghcr.io/ministryofjustice/devcontainer-feature/static-analysis:1": { + "version": "1.0.0", + "resolved": "ghcr.io/ministryofjustice/devcontainer-feature/static-analysis@sha256:e81d52725655c8ffb861605feac7ad155b447d51af65f6c3a03cab32d59f1e16", + "integrity": "sha256:e81d52725655c8ffb861605feac7ad155b447d51af65f6c3a03cab32d59f1e16" + } + } +} \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fa9f44e..8fbc8d4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,27 +1,31 @@ { - "name": "analytical-platform-ollamate", - "image": "ghcr.io/ministryofjustice/devcontainer-base:latest", - "features": { - "ghcr.io/devcontainers/features/node:1": { - "version": "20.11.1" - }, - "ghcr.io/devcontainers/features/python:1": { - "version": "3.12" - }, - "ghcr.io/devcontainers/features/docker-in-docker:2": {}, - "./features/src/postgresql": {}, - "ghcr.io/ministryofjustice/devcontainer-feature/aws:0": {} + "name": "analytical-platform-ollamate", + "image": "ghcr.io/ministryofjustice/devcontainer-base:latest", + "features": { + "ghcr.io/devcontainers/features/node:1": { + "version": "20.11.1" }, - "postCreateCommand": "bash scripts/devcontainer/post-create.sh", - "postStartCommand": "bash scripts/devcontainer/post-start.sh", - "runArgs": ["--name=analytical-platform-ollamate-devcontainer"], - "customizations": { - "vscode": { - "extensions": [ - "EditorConfig.EditorConfig", - "GitHub.vscode-github-actions", - "GitHub.vscode-codeql" - ] - } + "ghcr.io/devcontainers/features/python:1": { + "version": "3.12" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "./features/src/postgresql": {}, + "ghcr.io/ministryofjustice/devcontainer-feature/aws:1": {}, + "ghcr.io/ministryofjustice/devcontainer-feature/container-structure-test:1": {}, + "ghcr.io/ministryofjustice/devcontainer-feature/kubernetes:1": {}, + "ghcr.io/ministryofjustice/devcontainer-feature/static-analysis:1": {} + }, + "postCreateCommand": "bash scripts/devcontainer/post-create.sh", + "postStartCommand": "bash scripts/devcontainer/post-start.sh", + "runArgs": ["--name=analytical-platform-ollamate-devcontainer"], + "customizations": { + "vscode": { + "extensions": [ + "EditorConfig.EditorConfig", + "GitHub.vscode-github-actions", + "GitHub.vscode-codeql", + "ms-vsliveshare.vsliveshare" + ] } - } \ No newline at end of file + } +} diff --git a/.editorconfig b/.editorconfig index d159d74..fa1752a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,6 +5,19 @@ end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true +# This file is autogenerated +[.devcontainer/devcontainer-lock.json] +end_of_line = unset +insert_final_newline = unset + +[*.json] +indent_style = space +indent_size = 2 + [*.sh] indent_style = space -indent_size = 2 \ No newline at end of file +indent_size = 2 + +[{*.yml,*.yaml}] +indent_style = space +indent_size = 2 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..ceeb99e --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 88 +extend-ignore = E203, E704 +exclude = + venv diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ac066e6..c516797 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1 @@ -# Add a team or username to this file -# Example: -# * @ministryofjustice/operations-engineering +@ministryofjustice/analytical-platforms diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 15fe7f0..f5613dd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,21 +1,11 @@ --- -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file - version: 2 - updates: - - package-ecosystem: "bundler" + - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" - - package-ecosystem: "terraform" - directory: "/terraform" - schedule: - interval: "daily" - - package-ecosystem: "github-actions" + - package-ecosystem: "devcontainers" directory: "/" schedule: interval: "daily" @@ -27,10 +17,6 @@ updates: directory: "/" schedule: interval: "daily" - - package-ecosystem: "gomod" - directory: "/" - schedule: - interval: "daily" - package-ecosystem: "docker" directory: "/" schedule: diff --git a/.github/workflows/chart-lint.yml b/.github/workflows/chart-lint.yml new file mode 100644 index 0000000..e7393be --- /dev/null +++ b/.github/workflows/chart-lint.yml @@ -0,0 +1,33 @@ +--- +name: Chart Lint + +on: + pull_request: + branches: + - main + +permissions: {} + +jobs: + chart-lint: + name: Chart Lint + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + id: checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Set Up Helm + id: setup_helm + uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0 + + - name: Set Up Helm Chart Testing + id: setup_chart_testing + uses: helm/chart-testing-action@e6669bcd63d7cb57cb4380c33043eebe5d111992 # v2.6.1 + + - name: Lint Chart + id: lint_chart + run: | + make ct diff --git a/.github/workflows/enforce-version-pinning.yml b/.github/workflows/enforce-version-pinning.yml new file mode 100644 index 0000000..5ded51f --- /dev/null +++ b/.github/workflows/enforce-version-pinning.yml @@ -0,0 +1,38 @@ +--- +name: Enforce Version Pinning + +on: + pull_request: + branches: + - main + +permissions: {} + +jobs: + enforce-version-pinning: + name: Enforce Version Pinning + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check for pinned versions in requirements.txt + run: | + if grep -q -v '==' requirements.txt; then + echo "Unpinned dependencies found in requirements.txt" + echo "❌ Unpinned dependencies found in requirements.txt" + exit 1 + else + echo "✅ All dependencies are correctly pinned." + fi + + - name: Check for pinned versions in package.json + run: | + UNPINNED=$(grep -E '"[^"]+": "\^|~' package.json || true) + if [ -n "$UNPINNED" ]; then + echo "❌ Unpinned dependencies found in package.json:" + echo "$UNPINNED" + exit 1 + else + echo "✅ All dependencies are correctly pinned." + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a110715..feab62f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,54 +1,89 @@ --- -name: Release - -on: - push: - tags: - - '*.*.*' - -permissions: {} - -jobs: - release: - runs-on: ubuntu-latest - permissions: - contents: read - id-token: write - packages: write - steps: - - name: Checkout - id: checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Install cosign - id: install-cosign - uses: sigstore/cosign-installer@1fc5bd396d372bee37d608f955b336615edf79c8 # v3.2.0 - - - name: Login to GitHub Container Registry - id: login - uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Push - id: push - uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0 - with: - context: . - push: true - tags: ghcr.io/${{ github.repository_owner }}/analytical-platform-ui:${{ github.ref_name }} - - - name: Sign - id: sign - run: | - cosign sign --yes ghcr.io/${{ github.repository_owner }}/analytical-platform-ui@${{ steps.push.outputs.digest }} - - - name: Verify - id: verify - run: | - cosign verify \ - --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ - --certificate-identity=https://github.com/${{ github.repository_owner }}/analytical-platform-ui/.github/workflows/release.yml@refs/tags/${{ github.ref_name }} \ - ghcr.io/${{ github.repository_owner }}/analytical-platform-ui@${{ steps.push.outputs.digest }} + name: Release + + on: + push: + tags: + - "*" + + permissions: {} + + jobs: + release-image: + name: Release Image + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + packages: write + steps: + - name: Checkout + id: checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Install cosign + id: install_cosign + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 + + - name: Log in to GitHub Container Registry + id: login_ghcr + uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and Push + id: build_and_push + uses: docker/build-push-action@15560696de535e4014efeff63c48f16952e52dd1 # v6.2.0 + with: + push: true + tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }} + + - name: Sign + id: sign + shell: bash + run: | + cosign sign --yes ghcr.io/${{ github.repository }}@${{ steps.build_and_push.outputs.digest }} + + - name: Verify + id: verify + run: | + cosign verify \ + --certificate-oidc-issuer=https://token.actions.githubusercontent.com \ + --certificate-identity=https://github.com/${{ github.repository }}/.github/workflows/release.yml@refs/tags/${{ github.ref_name }} \ + ghcr.io/${{ github.repository }}@${{ steps.build_and_push.outputs.digest }} + + release-chart: + name: Release Chart + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + packages: write + steps: + - name: Checkout + id: checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Set Up Helm + id: setup_helm + uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0 + + - name: Log in to GitHub Container Registry + id: login_ghcr + uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Package Chart + id: package_chart + run: | + helm package chart --destination .helm-deploy + + - name: Push Chart + id: push_chart + run: | + helm push .helm-deploy/analytical-platform-ollamate-${{ github.ref_name }}.tgz oci://ghcr.io/ministryofjustice/analytical-platform-charts diff --git a/.github/workflows/scan-image.yml b/.github/workflows/scan-image.yml index de72c32..406193e 100644 --- a/.github/workflows/scan-image.yml +++ b/.github/workflows/scan-image.yml @@ -1,57 +1,57 @@ --- -name: Scan Image + name: Scan Image -on: - pull_request: - branches: - - main + on: + pull_request: + branches: + - main -permissions: {} + permissions: {} -jobs: - scan-image: - name: Scan Image - runs-on: ubuntu-latest - permissions: - contents: read - security-events: write - steps: - - name: Checkout - id: checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + jobs: + scan-image: + name: Scan Image + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - name: Checkout + id: checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - name: Build Image - id: build_image - uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0 - with: - push: false - load: true - tags: dashboard + - name: Build Image + id: build_image + uses: docker/build-push-action@15560696de535e4014efeff63c48f16952e52dd1 # v6.2.0 + with: + push: false + load: true + tags: ollamate - - name: Scan Image - id: scan_image - uses: aquasecurity/trivy-action@062f2592684a31eb3aa050cc61e7ca1451cecd3d # v0.18.0 - with: - image-ref: dashboard - exit-code: 1 - format: sarif - output: trivy-results.sarif - severity: CRITICAL - limit-severities-for-sarif: true + - name: Scan Image + id: scan_image + uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # v0.23.0 + with: + image-ref: ollamate + exit-code: 1 + format: sarif + output: trivy-results.sarif + severity: CRITICAL + limit-severities-for-sarif: true - - name: Scan Image (On SARIF Scan Failure) - if: failure() && steps.scan_image.outcome == 'failure' - id: scan_image_on_failure - uses: aquasecurity/trivy-action@062f2592684a31eb3aa050cc61e7ca1451cecd3d # v0.18.0 - with: - image-ref: dashboard - exit-code: 1 - format: table - severity: CRITICAL + - name: Scan Image (On SARIF Scan Failure) + if: failure() && steps.scan_image.outcome == 'failure' + id: scan_image_on_failure + uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # v0.23.0 + with: + image-ref: ollamate + exit-code: 1 + format: table + severity: CRITICAL - - name: Upload SARIF - if: always() - id: upload_sarif - uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.2.7 - with: - sarif_file: trivy-results.sarif + - name: Upload SARIF + if: always() + id: upload_sarif + uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v2.2.7 + with: + sarif_file: trivy-results.sarif diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5577601 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,45 @@ +--- +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - name: End of File Fixer + id: end-of-file-fixer + - name: Trailing Whitespace Fixer + id: trailing-whitespace + - name: Check yaml + id: check-yaml + - name: requirements.txt fixer + id: requirements-txt-fixer + + - repo: https://github.com/psf/black + rev: 23.10.1 + hooks: + - id: black + name: black formatting + + - repo: https://github.com/PyCQA/flake8 + rev: 6.1.0 + hooks: + - id: flake8 + name: flake8 lint + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.6.1 + hooks: + - id: mypy + name: mypy + additional_dependencies: + - django-stubs + - psycopg2-binary + + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + name: isort (python) + + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.32.0 + hooks: + - id: yamllint diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c829c6d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,42 @@ +# FROM public.ecr.aws/docker/library/node:20.11.1 AS build-node + +# WORKDIR / +# COPY package.json package-lock.json ./ +# COPY assets/scss/app.scss ./assets/scss/app.scss + +# RUN npm install \ +# && npm run css + +FROM public.ecr.aws/docker/library/python:3.12-alpine3.19 AS base + +RUN apk add --no-cache --virtual .build-deps \ + libffi-dev=3.4.4-r3 \ + gcc=13.2.1_git20231014-r0 \ + musl-dev=1.2.4_git20230717-r4 \ + && apk add --no-cache libpq-dev=16.3-r0 + +WORKDIR /ollamate + +RUN mkdir --parents static/assets/fonts \ + && mkdir --parents static/assets/images \ + && mkdir --parents static/assets/js + +# COPY --from=build-node static/app.css static/app.css +# COPY --from=build-node node_modules/govuk-frontend/dist/govuk/assets/fonts/. static/assets/fonts +# COPY --from=build-node node_modules/govuk-frontend/dist/govuk/assets/images/. static/assets/images +# COPY --from=build-node node_modules/govuk-frontend/dist/govuk/all.bundle.js static/assets/js/govuk.js +COPY scripts/container/entrypoint.sh /usr/local/bin/entrypoint.sh +COPY requirements.txt manage.py ./ +COPY ollamate ollamate +COPY tests tests + +RUN pip install --no-cache-dir --requirement requirements.txt \ + && chmod +x /usr/local/bin/entrypoint.sh \ + && python manage.py collectstatic --noinput --ignore=*.scss \ + && apk del .build-deps + +EXPOSE 8000 + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] + +CMD ["run"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c3916a7 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +#!make + +db-migrate: + python manage.py migrate + +db-drop: + python manage.py reset_db + +serve: + python manage.py runserver + +container: + docker build -t ollamate . + +test: container + @echo + @echo "> Running Python Tests (In Docker)..." + IMAGE_TAG=ollamate docker compose --file=contrib/docker-compose-test.yml run --rm interfaces + +ct: + ct lint --charts chart diff --git a/README.md b/README.md index a6fbc77..9eb1f26 100644 --- a/README.md +++ b/README.md @@ -2,65 +2,16 @@ [![repo standards badge](https://img.shields.io/endpoint?labelColor=231f20&color=005ea5&style=for-the-badge&label=MoJ%20Compliant&url=https%3A%2F%2Foperations-engineering-reports.cloud-platform.service.justice.gov.uk%2Fapi%2Fv1%2Fcompliant_public_repositories%2Fendpoint%2Ftemplate-repository&logo=data:image/png;base64,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)](https://operations-engineering-reports.cloud-platform.service.justice.gov.uk/public-report/template-repository) -This template repository equips you with the default initial files required for a Ministry of Justice GitHub repository. +> [!NOTE] +> This repository is a proof of concept application. -## Included Files +## Analytical Platform Ollamate 🦙🧉 -The repository comes with the following preset files: +This repository includes a Django application which presents a simple frontend to a user which allows them to consume an LLM running from Ollama. -- LICENSE -- .gitignore -- CODEOWNERS -- dependabot.yml -- GitHub Actions example files -- Ministry of Justice Compliance Badge (public repositories only) +The name 'Ollamate' is derived from Ollama and will be changed if/when this application evolves. -## Setup Instructions +## Getting Started -Once you've created your repository using this template, ensure the following steps: - -### Update README - -Edit this README.md file to document your project accurately. Take the time to create a clear, engaging, and informative README.md file. Include information like what your project does, how to install and run it, how to contribute, and any other pertinent details. - -### Update repository description - -After you've created your repository, GitHub provides a brief description field that appears on the top of your repository's main page. This is a summary that gives visitors quick insight into the project. Using this field to provide a succinct overview of your repository is highly recommended. - -This description and your README.md will be one of the first things people see when they visit your repository. It's a good place to make a strong, concise first impression. Remember, this is often visible in search results on GitHub and search engines, so it's also an opportunity to help people discover your project. - -### Grant Team Permissions - -Assign permissions to the appropriate Ministry of Justice teams. Ensure at least one team is granted Admin permissions. Whenever possible, assign permissions to teams rather than individual users. - -### Read about the GitHub repository standards - -Familiarise yourself with the Ministry of Justice GitHub Repository Standards. These standards ensure consistency, maintainability, and best practices across all our repositories. - -You can find the standards [here](https://user-guide.operations-engineering.service.justice.gov.uk/documentation/information/mojrepostandards.html). - -Please read and understand these standards thoroughly and enable them when you feel comfortable. - -### Modify the GitHub Standards Badge - -Once you've ensured that all the [GitHub Repository Standards](https://user-guide.operations-engineering.service.justice.gov.uk/documentation/information/mojrepostandards.html) have been applied to your repository, it's time to update the Ministry of Justice (MoJ) Compliance Badge located in the README file. - -The badge demonstrates that your repository is compliant with MoJ's standards. Please follow these [instructions](https://user-guide.operations-engineering.service.justice.gov.uk/documentation/information/add-repo-badge.html) to modify the badge URL to reflect the status of your repository correctly. - -**Please note** the badge will not function correctly if your repository is internal or private. In this case, you may remove the badge from your README. - -### Manage Outside Collaborators - -To add an Outside Collaborator to the repository, follow the guidelines detailed [here](https://github.com/ministryofjustice/github-collaborators). - -### Update CODEOWNERS - -(Optional) Modify the CODEOWNERS file to specify the teams or users authorized to approve pull requests. - -### Configure Dependabot - -Adapt the dependabot.yml file to match your project's [dependency manager](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem) and to enable [automated pull requests for package updates](https://docs.github.com/en/code-security/supply-chain-security). - -### Dependency Review - -If your repository is private with no GitHub Advanced Security license, remove the `.github/workflows/dependency-review.yml` file. +> [!NOTE] +> 🚧 WIP 🚧 diff --git a/chart/.helmignore b/chart/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/chart/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 0000000..45b5c66 --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: v2 +name: analytical-platform-ollamate +description: Analytical Platform Ollamate +type: application +version: 0.0.1 +appVersion: 0.0.1 +icon: https://upload.wikimedia.org/wikipedia/en/thumb/4/4a/Ministry_of_Justice_logo_%28United_Kingdom%29.svg/611px-Ministry_of_Justice_logo_%28United_Kingdom%29.svg.png +maintainers: + - name: moj-data-platform-robot + email: analytical-platform@digital.justice.gov.uk diff --git a/chart/ci/lint-values.yaml b/chart/ci/lint-values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/chart/templates/.helpers.tpl b/chart/templates/.helpers.tpl new file mode 100644 index 0000000..3e33227 --- /dev/null +++ b/chart/templates/.helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "actions-runner.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "actions-runner.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "actions-runner.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "actions-runner.labels" -}} +helm.sh/chart: {{ include "actions-runner.chart" . }} +{{ include "actions-runner.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "actions-runner.selectorLabels" -}} +app.kubernetes.io/name: {{ include "actions-runner.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "actions-runner.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "actions-runner.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/chart/templates/deployment.yml b/chart/templates/deployment.yml new file mode 100644 index 0000000..582d8c6 --- /dev/null +++ b/chart/templates/deployment.yml @@ -0,0 +1,49 @@ +{{- if not .Values.ephemeral.enabled }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "actions-runner.fullname" . }} + labels: + {{- include "actions-runner.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "actions-runner.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "actions-runner.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "actions-runner.serviceAccountName" . }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: GITHUB_REPOSITORY + value: {{ .Values.github.organisation }}/{{ .Values.github.repository }} + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-github-token + key: token + - name: RUNNER_LABELS + value: {{ .Values.github.runner.labels | quote }} + resources: + {{- toYaml .Values.resources | nindent 12 }} +{{- end }} diff --git a/chart/templates/scaled-job.yaml b/chart/templates/scaled-job.yaml new file mode 100644 index 0000000..582d8c6 --- /dev/null +++ b/chart/templates/scaled-job.yaml @@ -0,0 +1,49 @@ +{{- if not .Values.ephemeral.enabled }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "actions-runner.fullname" . }} + labels: + {{- include "actions-runner.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "actions-runner.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "actions-runner.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "actions-runner.serviceAccountName" . }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: GITHUB_REPOSITORY + value: {{ .Values.github.organisation }}/{{ .Values.github.repository }} + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-github-token + key: token + - name: RUNNER_LABELS + value: {{ .Values.github.runner.labels | quote }} + resources: + {{- toYaml .Values.resources | nindent 12 }} +{{- end }} diff --git a/chart/templates/secret-account.yaml b/chart/templates/secret-account.yaml new file mode 100644 index 0000000..e207736 --- /dev/null +++ b/chart/templates/secret-account.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "actions-runner.serviceAccountName" . }} + labels: + {{- include "actions-runner.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} diff --git a/chart/templates/secret.yaml b/chart/templates/secret.yaml new file mode 100644 index 0000000..352ffd0 --- /dev/null +++ b/chart/templates/secret.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-github-token + labels: + {{- include "actions-runner.labels" . | nindent 4 }} +type: Opaque +data: + token: {{ .Values.github.token | b64enc }} diff --git a/chart/values.yaml b/chart/values.yaml new file mode 100644 index 0000000..033b5eb --- /dev/null +++ b/chart/values.yaml @@ -0,0 +1,49 @@ +--- +nameOverride: "" +fullnameOverride: "" + +replicaCount: 1 + +image: + pullPolicy: IfNotPresent + repository: ghcr.io/ministryofjustice/analytical-platform-ollamate + tag: 0.0.1 + +imagePullSecrets: [] + +serviceAccount: + create: true + automount: true + name: "" + annotations: {} + +podAnnotations: {} +podLabels: {} + +securityContext: + runAsNonRoot: true + runAsUser: 10000 + +resources: + requests: + cpu: 1 + memory: "5Gi" + limits: + cpu: 2 + memory: "7Gi" + +github: + organisation: + repository: + token: + runner: + labels: + +ephemeral: + enabled: false + karpenter: + enabled: false + nodePool: "general-on-demand" + keda: + maxReplicaCount: 5 + pollingInterval: 30 diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..576b950 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,33 @@ +upstream django { + server localhost:8000; +} + +server { + listen 8080; + + location /nginx-health { + access_log off; + return 200 "healthy\n"; + } + + location /static/ { + root /usr/share/nginx/html; + } + + location / { + try_files $uri @proxy_to_django; + } + + location @proxy_to_django { + proxy_pass http://django; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } +} diff --git a/ollamate/settings.py b/ollamate/settings.py index c02d8e7..ea65907 100644 --- a/ollamate/settings.py +++ b/ollamate/settings.py @@ -47,6 +47,7 @@ ALLOWED_HOSTS = [] +PROJECT_NAME = "ollamate" # Application definition @@ -95,13 +96,25 @@ # Database # https://docs.djangoproject.com/en/5.0/ref/settings/#databases -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', +DB_HOST = os.environ.get("DB_HOST", "127.0.0.1") +ENABLE_DB_SSL = ( + str(os.environ.get("ENABLE_DB_SSL", DB_HOST not in ["127.0.0.1", "localhost"])).lower() + == "true" +) +DATABASES: dict = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": os.environ.get("DB_NAME", PROJECT_NAME), + "USER": os.environ.get("DB_USER", ""), + "PASSWORD": os.environ.get("DB_PASSWORD", ""), + "HOST": DB_HOST, + "PORT": os.environ.get("DB_PORT", "5432"), } } +if ENABLE_DB_SSL: + DATABASES["default"]["OPTIONS"] = {"sslmode": "require"} + # Password validation # https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ace255c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "analytical-platform-ollamate", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/requirements.dev.txt b/requirements.dev.txt new file mode 100644 index 0000000..c2497dd --- /dev/null +++ b/requirements.dev.txt @@ -0,0 +1,7 @@ +-r ./requirements.txt +black==23.10.1 +django-stubs[compatible-mypy]==4.2.6 +flake8==6.1.0 +isort==5.12.0 +mypy==1.6.1 +pre-commit==3.5.0 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..509a6ab --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +django-azure-auth==2.0.0 +django-environ==0.11.2 +requests==2.32.3 +psycopg==3.2.1 diff --git a/scripts/container/entrypoint.sh b/scripts/container/entrypoint.sh new file mode 100644 index 0000000..157e723 --- /dev/null +++ b/scripts/container/entrypoint.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env sh + +MODE=${MODE:-"run"} +ADDRESS=${ADDRESS:-"0.0.0.0"} +PORT=${PORT:-"8000"} +WORKERS=${WORKERS:-"4"} + +case "$MODE" in +"run") + echo "Running Django server on ${ADDRESS}:${PORT}" + gunicorn -b "${ADDRESS}":"${PORT}" -k uvicorn.workers.UvicornWorker -w "${WORKERS}" ap.asgi:application + ;; +"migrate") + echo "Running Django migrations" + python manage.py migrate + ;; +*) + echo "Unknown mode: ${MODE}" + exit 1 + ;; +esac diff --git a/scripts/devcontainer/post-create.sh b/scripts/devcontainer/post-create.sh new file mode 100644 index 0000000..f412ff3 --- /dev/null +++ b/scripts/devcontainer/post-create.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# Upgrade NPM +npm install --global npm@latest + +# Start Postgres +docker compose --file contrib/docker-compose-postgres.yml up --detach + +# Upgrade Pip +pip install --break-system-package --upgrade pip + +# Install dependencies +pip install --break-system-package --requirement requirements.dev.txt + +# install npm dependencies and static assets +# npm install +# make build-static + +# Run migrations +python manage.py migrate diff --git a/scripts/devcontainer/post-start.sh b/scripts/devcontainer/post-start.sh new file mode 100644 index 0000000..f0289d1 --- /dev/null +++ b/scripts/devcontainer/post-start.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# Run pre-commit +pre-commit install