diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..760805f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git* +*.md +build* +docker-compose* +env +test-configuration +.netpoint/.git* +.netpoint/contrib +.netpoint/scripts +.netpoint/upgrade.sh diff --git a/.ecrc b/.ecrc new file mode 100644 index 0000000..f0c91f7 --- /dev/null +++ b/.ecrc @@ -0,0 +1,23 @@ +{ + "Verbose": false, + "Debug": false, + "IgnoreDefaults": false, + "SpacesAftertabs": false, + "NoColor": false, + "Exclude": [ + "LICENSE", + "\\.initializers", + "\\.vscode" + ], + "AllowedContentTypes": [], + "PassedFiles": [], + "Disable": { + // set these options to true to disable specific checks + "EndOfLine": false, + "Indentation": false, + "InsertFinalNewline": false, + "TrimTrailingWhitespace": false, + "IndentSize": true, + "MaxLineLength": false + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7f9f55d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 + +[*.py] +indent_size = 4 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..83a86a2 --- /dev/null +++ b/.flake8 @@ -0,0 +1,7 @@ +[flake8] +max-line-length = 100 +extend-ignore = E203, W503 +per-file-ignores = + configuration/*:E131,E251,E266,E302,E305,E501,E722 + startup_scripts/startup_script_utils/__init__.py:F401 + docker/*:E266,E722 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..acdb4e0 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,14 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +- cimnine +- tobiasge +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..14d810b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,148 @@ +name: Bug report +description: Create a report about a malfunction of the Docker setup +body: +- type: markdown + attributes: + value: | + Please only raise an issue if you're certain that you've found a bug. + Else, see these other means to get help: + + - See our troubleshooting section: + https://github.com/khulnasoft/netpoint-docker/wiki/Troubleshooting + - Have a look at the rest of the wiki: + https://github.com/khulnasoft/netpoint-docker/wiki + - Check the release notes: + https://github.com/khulnasoft/netpoint-docker/releases + - Look through the issues already resolved: + https://github.com/khulnasoft/netpoint-docker/issues?q=is%3Aclosed + + If you did not find what you're looking for, + try the help of our community: + + - Post to Github Discussions: + https://github.com/khulnasoft/netpoint-docker/discussions + - Join the `#netpoint-docker` channel on our Slack: + https://join.slack.com/t/netdev-community/shared_invite/zt-mtts8g0n-Sm6Wutn62q_M4OdsaIycrQ + - Ask on the NetPoint mailing list: + https://groups.google.com/d/forum/netpoint-discuss + + Please don't open an issue to open a PR. + Just submit the PR, that's good enough. +- type: textarea + id: current-behavior + attributes: + label: Current Behavior + description: Please describe what you did and how you think it misbehaved + placeholder: I tried to … by doing …, but it … + validations: + required: true +- type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: Please describe what you expected instead + placeholder: I expected that … when I do … + validations: + required: true +- type: input + id: docker-compose-version + attributes: + label: Docker Compose Version + description: Please paste the output of `docker-compose version` + placeholder: Docker Compose version vX.Y.Z + validations: + required: true +- type: textarea + id: docker-version + attributes: + label: Docker Version + description: Please paste the output of `docker version` + render: text + placeholder: | + Client: + Cloud integration: 1.0.17 + Version: 20.10.8 + API version: 1.41 + Go version: go1.16.6 + Git commit: 3967b7d + Built: Fri Jul 30 19:55:20 2021 + OS/Arch: darwin/amd64 + Context: default + Experimental: true + + Server: Docker Engine - Community + Engine: + Version: 20.10.8 + API version: 1.41 (minimum version 1.12) + Go version: go1.16.6 + Git commit: 75249d8 + Built: Fri Jul 30 19:52:10 2021 + OS/Arch: linux/amd64 + Experimental: false + containerd: + Version: 1.4.9 + GitCommit: e25210fe30a0a703442421b0f60afac609f950a3 + runc: + Version: 1.0.1 + GitCommit: v1.0.1-0-g4144b63 + docker-init: + Version: 0.19.0 + GitCommit: de40ad0 + validations: + required: true +- type: input + id: git-rev + attributes: + label: The git Revision + description: Please paste the output of `git rev-parse HEAD` + validations: + required: true +- type: textarea + id: git-status + attributes: + label: The git Status + description: Please paste the output of `git status` + render: text + placeholder: | + On branch main + nothing to commit, working tree clean + validations: + required: true +- type: input + id: run-command + attributes: + label: Startup Command + description: Please specify the command you used to start the project + placeholder: docker compose up + validations: + required: true +- type: textarea + id: netpoint-logs + attributes: + label: NetPoint Logs + description: Please paste the output of `docker-compose logs netpoint` (or `docker compose logs netpoint`) + render: text + placeholder: | + netpoint_1 | ⚙️ Applying database migrations + netpoint_1 | 🧬 loaded config '/etc/netpoint/config/configuration.py' + netpoint_1 | 🧬 loaded config '/etc/netpoint/config/a.py' + netpoint_1 | 🧬 loaded config '/etc/netpoint/config/extra.py' + netpoint_1 | 🧬 loaded config '/etc/netpoint/config/logging.py' + netpoint_1 | 🧬 loaded config '/etc/netpoint/config/plugins.py' + ... + validations: + required: true +- type: textarea + id: docker-compose-override-yml + attributes: + label: Content of docker-compose.override.yml + description: Please paste the output of `cat docker-compose.override.yml` + render: yaml + placeholder: | + version: '3.4' + services: + netpoint: + ports: + - '8080:8080' + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..2511fc8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,15 @@ +blank_issues_enabled: false +contact_links: + - name: Question + url: https://github.com/khulnasoft/netpoint-docker/discussions + about: The Github Discussions are the right place to ask questions about how to use or do certain things with NetPoint Docker. + + - name: Chat + url: https://join.slack.com/t/netdev-community/shared_invite/zt-mtts8g0n-Sm6Wutn62q_M4OdsaIycrQ + about: 'Usually the quickest way to seek help with small issues is to join our #netpoint-docker Slack channel.' + + - name: Community Wiki + url: https://github.com/khulnasoft/netpoint-docker/wiki + about: | + Our wiki contains information for common problems and tips for operating NetPoint Docker in production. + It's maintained by our excellent community. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..7808abd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,68 @@ +name: Feature or Change Request +description: Request a new feature or a change of the current behavior +body: +- type: markdown + attributes: + value: | + This issue type is to propose new features for the Docker setup. + To just spin an idea, see the Github Discussions section, please. + + Before asking for help, see these links first: + + - See our troubleshooting section: + https://github.com/khulnasoft/netpoint-docker/wiki/Troubleshooting + - Have a look at the rest of the wiki: + https://github.com/khulnasoft/netpoint-docker/wiki + - Check the release notes: + https://github.com/khulnasoft/netpoint-docker/releases + - Look through the issues already resolved: + https://github.com/khulnasoft/netpoint-docker/issues?q=is%3Aclosed + + If you did not find what you're looking for, + try the help of our community: + + - Post to Github Discussions: + https://github.com/khulnasoft/netpoint-docker/discussions + - Join the `#netpoint-docker` channel on our Slack: + https://join.slack.com/t/netdev-community/shared_invite/zt-mtts8g0n-Sm6Wutn62q_M4OdsaIycrQ + - Ask on the NetPoint mailing list: + https://groups.google.com/d/forum/netpoint-discuss + + Please don't open an issue to open a PR. + Just submit the PR, that's good enough. +- type: textarea + id: desired-behavior + attributes: + label: Desired Behavior + description: Please describe the desired behavior + placeholder: To me, it would be useful, if … because … + validations: + required: true +- type: textarea + id: contrast-to-current + attributes: + label: Contrast to Current Behavior + description: Please describe how the desired behavior is different from the current behavior + placeholder: The current behavior is …, but this lacks … + validations: + required: true +- type: textarea + id: required-changes + attributes: + label: Required Changes + description: If you can, please elaborate what changes will be required to implement the desired behavior + placeholder: I suggest to change the file … + validations: + required: false +- type: textarea + id: discussion + attributes: + label: 'Discussion: Benefits and Drawbacks' + description: | + Please make your case here: + - Why do you think this project and the community will benefit from your suggestion? + - What are the drawbacks of this change? Is it backwards-compatible? + - Anything else that you think is relevant to the discussion of this feature/change request. + placeholder: I suggest to change the file … + validations: + required: false diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 0000000..1a7a45a --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,10 @@ +# Configuration for probot-no-response - https://github.com/probot/no-response + +# Number of days of inactivity before an Issue is closed for lack of response +daysUntilClose: 30 +# Label requiring a response +responseRequiredLabel: awaiting answer +# Comment to post when closing an Issue for lack of response. Set to `false` to disable +closeComment: > + This issue has been automatically closed because there has been no response + to our request for more information from the original author. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..139ff96 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,85 @@ + + + + +Related Issue: + +## New Behavior + + + +... + +## Contrast to Current Behavior + + + +... + +## Discussion: Benefits and Drawbacks + + + +... + +## Changes to the Wiki + + + +... + +## Proposed Release Note Entry + + + +... + +## Double Check + + + +* [ ] I have read the comments and followed the PR template. +* [ ] I have explained my PR according to the information in the comments. +* [ ] My PR targets the `develop` branch. diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 0000000..cb7150f --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,86 @@ +--- +name: push + +on: + push: + branches-ignore: + - release + pull_request: + branches-ignore: + - release + +jobs: + lint: + runs-on: ubuntu-latest + name: Checks syntax of our code + steps: + - uses: actions/checkout@v4 + with: + # Full git history is needed to get a proper + # list of changed files within `super-linter` + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: '3.9' + - name: Lint Code Base + uses: github/super-linter@v5 + env: + DEFAULT_BRANCH: develop + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SUPPRESS_POSSUM: true + LINTER_RULES_PATH: / + VALIDATE_ALL_CODEBASE: false + VALIDATE_DOCKERFILE: false + VALIDATE_GITLEAKS: false + FILTER_REGEX_EXCLUDE: (.*/)?(LICENSE|configuration/.*) + EDITORCONFIG_FILE_NAME: .ecrc + DOCKERFILE_HADOLINT_FILE_NAME: .hadolint.yaml + MARKDOWN_CONFIG_FILE: .markdown-lint.yml + PYTHON_BLACK_CONFIG_FILE: pyproject.toml + PYTHON_FLAKE8_CONFIG_FILE: .flake8 + PYTHON_ISORT_CONFIG_FILE: pyproject.toml + YAML_CONFIG_FILE: .yamllint.yaml + build: + continue-on-error: ${{ matrix.build_cmd != './build-latest.sh' }} + strategy: + matrix: + build_cmd: + - ./build-latest.sh + - PRERELEASE=true ./build-latest.sh + - ./build.sh feature + - ./build.sh develop + platform: + - linux/amd64 + - linux/arm64 + fail-fast: false + env: + GH_ACTION: enable + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + IMAGE_NAMES: docker.io/khulnasoft/netpoint + runs-on: ubuntu-latest + name: Builds new NetPoint Docker Images + steps: + - id: git-checkout + name: Checkout + uses: actions/checkout@v4 + - id: qemu-setup + name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - id: buildx-setup + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - id: docker-build + name: Build the image for '${{ matrix.platform }}' with '${{ matrix.build_cmd }}' + run: ${{ matrix.build_cmd }} + env: + BUILDX_PLATFORM: ${{ matrix.platform }} + BUILDX_BUILDER_NAME: ${{ steps.buildx-setup.outputs.name }} + - id: arm-time-limit + name: Set Netbox container start_period higher on ARM64 + if: matrix.platform == 'linux/arm64' + run: | + echo "NETBOX_START_PERIOD=240s" >>"${GITHUB_ENV}" + - id: docker-test + name: Test the image + run: IMAGE="${FINAL_DOCKER_TAG}" ./test.sh + if: steps.docker-build.outputs.skipped != 'true' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..dc00d72 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,84 @@ +--- +name: release + +on: + release: + types: + - published + schedule: + - cron: '45 5 * * *' + workflow_dispatch: + +jobs: + build: + strategy: + matrix: + build_cmd: + - ./build-latest.sh + - PRERELEASE=true ./build-latest.sh + - ./build.sh feature + - ./build.sh develop + platform: + - linux/amd64,linux/arm64 + fail-fast: false + runs-on: ubuntu-latest + name: Builds new NetPoint Docker Images + env: + GH_ACTION: enable + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + IMAGE_NAMES: docker.io/khulnasoft/netpoint quay.io/khulnasoft/netpoint ghcr.io/khulnasoft/netpoint + steps: + - id: source-checkout + name: Checkout + uses: actions/checkout@v4 + - id: set-netpoint-docker-version + name: Get Version of NetPoint Docker + run: echo "version=$(cat VERSION)" >>"$GITHUB_OUTPUT" + shell: bash + - id: qemu-setup + name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - id: buildx-setup + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - id: docker-build + name: Build the image with '${{ matrix.build_cmd }}' + run: ${{ matrix.build_cmd }} + - id: test-image + name: Test the image + run: IMAGE="${FINAL_DOCKER_TAG}" ./test.sh + if: steps.docker-build.outputs.skipped != 'true' + # docker.io + - id: docker-io-login + name: Login to docker.io + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.dockerhub_username }} + password: ${{ secrets.dockerhub_password }} + if: steps.docker-build.outputs.skipped != 'true' + # quay.io + - id: quay-io-login + name: Login to Quay.io + uses: docker/login-action@v3 + with: + registry: quay.io + username: ${{ secrets.quayio_username }} + password: ${{ secrets.quayio_password }} + if: steps.docker-build.outputs.skipped != 'true' + # ghcr.io + - id: ghcr-io-login + name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + if: steps.docker-build.outputs.skipped != 'true' + - id: build-and-push + name: Push the image + run: ${{ matrix.build_cmd }} --push + if: steps.docker-build.outputs.skipped != 'true' + env: + BUILDX_PLATFORM: ${{ matrix.platform }} + BUILDX_BUILDER_NAME: ${{ steps.buildx-setup.outputs.name }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c4499f --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.sql.gz +.netpoint +.python-version +docker-compose.override.yml +*.pem +configuration/* +!configuration/configuration.py +!configuration/extra.py +configuration/ldap/* +!configuration/ldap/extra.py +!configuration/ldap/ldap_config.py +!configuration/logging.py +!configuration/plugins.py +super-linter.log diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000..4442bd4 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,4 @@ +ignored: +- DL3006 +- DL3008 +- DL3003 diff --git a/.markdown-lint.yml b/.markdown-lint.yml new file mode 100644 index 0000000..beebc4b --- /dev/null +++ b/.markdown-lint.yml @@ -0,0 +1,2 @@ +MD013: false +MD041: false diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..50d6af7 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,5 @@ +--- + +rules: + line-length: + max: 120 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..555dd34 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,127 @@ +ARG FROM +FROM ${FROM} as builder + +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update -qq \ + && apt-get upgrade \ + --yes -qq --no-install-recommends \ + && apt-get install \ + --yes -qq --no-install-recommends \ + build-essential \ + ca-certificates \ + libldap-dev \ + libpq-dev \ + libsasl2-dev \ + libssl-dev \ + libxml2-dev \ + libxmlsec1 \ + libxmlsec1-dev \ + libxmlsec1-openssl \ + libxslt-dev \ + pkg-config \ + python3-dev \ + python3-pip \ + python3-venv \ + && python3 -m venv /opt/netpoint/venv \ + && /opt/netpoint/venv/bin/python3 -m pip install --upgrade \ + pip \ + setuptools \ + wheel + +ARG NETBOX_PATH +COPY ${NETBOX_PATH}/requirements.txt requirements-container.txt / +RUN \ + # We compile 'psycopg' in the build process + sed -i -e '/psycopg/d' /requirements.txt && \ + # Gunicorn is not needed because we use Nginx Unit + sed -i -e '/gunicorn/d' /requirements.txt && \ + # We need 'social-auth-core[all]' in the Docker image. But if we put it in our own requirements-container.txt + # we have potential version conflicts and the build will fail. + # That's why we just replace it in the original requirements.txt. + sed -i -e 's/social-auth-core\[openidconnect\]/social-auth-core\[all\]/g' /requirements.txt && \ + /opt/netpoint/venv/bin/pip install \ + -r /requirements.txt \ + -r /requirements-container.txt + +### +# Main stage +### + +ARG FROM +FROM ${FROM} as main + +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update -qq \ + && apt-get upgrade \ + --yes -qq --no-install-recommends \ + && apt-get install \ + --yes -qq --no-install-recommends \ + bzip2 \ + ca-certificates \ + curl \ + libldap-common \ + libpq5 \ + libxmlsec1-openssl \ + openssh-client \ + openssl \ + python3 \ + python3-distutils \ + tini \ + && curl --silent --output /usr/share/keyrings/nginx-keyring.gpg \ + https://unit.nginx.org/keys/nginx-keyring.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] https://packages.nginx.org/unit/ubuntu/ lunar unit" \ + > /etc/apt/sources.list.d/unit.list \ + && apt-get update -qq \ + && apt-get install \ + --yes -qq --no-install-recommends \ + unit=1.31.1-1~lunar \ + unit-python3.11=1.31.1-1~lunar \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=builder /opt/netpoint/venv /opt/netpoint/venv + +ARG NETBOX_PATH +COPY ${NETBOX_PATH} /opt/netpoint +# Copy the modified 'requirements*.txt' files, to have the files actually used during installation +COPY --from=builder /requirements.txt /requirements-container.txt /opt/netpoint/ + +COPY docker/configuration.docker.py /opt/netpoint/netpoint/netpoint/configuration.py +COPY docker/ldap_config.docker.py /opt/netpoint/netpoint/netpoint/ldap_config.py +COPY docker/docker-entrypoint.sh /opt/netpoint/docker-entrypoint.sh +COPY docker/housekeeping.sh /opt/netpoint/housekeeping.sh +COPY docker/launch-netpoint.sh /opt/netpoint/launch-netpoint.sh +COPY configuration/ /etc/netpoint/config/ +COPY docker/nginx-unit.json /etc/unit/ + +WORKDIR /opt/netpoint/netpoint + +# Must set permissions for '/opt/netpoint/netpoint/media' directory +# to g+w so that pictures can be uploaded to netpoint. +RUN mkdir -p static /opt/unit/state/ /opt/unit/tmp/ \ + && chown -R unit:root /opt/unit/ media reports scripts \ + && chmod -R g+w /opt/unit/ media reports scripts \ + && cd /opt/netpoint/ && SECRET_KEY="dummyKeyWithMinimumLength-------------------------" /opt/netpoint/venv/bin/python -m mkdocs build \ + --config-file /opt/netpoint/mkdocs.yml --site-dir /opt/netpoint/netpoint/project-static/docs/ \ + && SECRET_KEY="dummyKeyWithMinimumLength-------------------------" /opt/netpoint/venv/bin/python /opt/netpoint/netpoint/manage.py collectstatic --no-input + +ENV LANG=C.utf8 PATH=/opt/netpoint/venv/bin:$PATH +ENTRYPOINT [ "/usr/bin/tini", "--" ] + +CMD [ "/opt/netpoint/docker-entrypoint.sh", "/opt/netpoint/launch-netpoint.sh" ] + +LABEL netpoint.original-tag="" \ + netpoint.git-branch="" \ + netpoint.git-ref="" \ + netpoint.git-url="" \ +# See https://github.com/opencontainers/image-spec/blob/master/annotations.md#pre-defined-annotation-keys + org.opencontainers.image.created="" \ + org.opencontainers.image.title="NetPoint Docker" \ + org.opencontainers.image.description="A container based distribution of NetPoint, the free and open IPAM and DCIM solution." \ + org.opencontainers.image.licenses="Apache-2.0" \ + org.opencontainers.image.authors="The netpoint-docker contributors." \ + org.opencontainers.image.vendor="The netpoint-docker contributors." \ + org.opencontainers.image.url="https://github.com/khulnasoft/netpoint-docker" \ + org.opencontainers.image.documentation="https://github.com/khulnasoft/netpoint-docker/wiki" \ + org.opencontainers.image.source="https://github.com/khulnasoft/netpoint-docker.git" \ + org.opencontainers.image.revision="" \ + org.opencontainers.image.version="" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e454a52 --- /dev/null +++ b/LICENSE @@ -0,0 +1,178 @@ + + 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 + diff --git a/README.md b/README.md index dc76791..bbdd5ab 100644 --- a/README.md +++ b/README.md @@ -1 +1,160 @@ # netpoint-docker + +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/khulnasoft/netpoint-docker)][github-release] +[![GitHub stars](https://img.shields.io/github/stars/khulnasoft/netpoint-docker)][github-stargazers] +![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed-raw/khulnasoft/netpoint-docker) +![Github release workflow](https://img.shields.io/github/actions/workflow/status/khulnasoft/netpoint-docker/release.yml?branch=release) +![Docker Pulls](https://img.shields.io/docker/pulls/khulnasoft/netpoint) +[![GitHub license](https://img.shields.io/github/license/khulnasoft/netpoint-docker)][netpoint-docker-license] + +[The GitHub repository][netpoint-docker-github] houses the components needed to build NetPoint as a container. +Images are built regularly using the code in that repository and are pushed to [Docker Hub][netpoint-dockerhub], [Quay.io][netpoint-quayio] and [GitHub Container Registry][netpoint-ghcr]. + +Do you have any questions? +Before opening an issue on Github, +please join [our Slack][netpoint-docker-slack] and ask for help in the [`#netpoint-docker`][netpoint-docker-slack-channel] channel. + +[github-stargazers]: https://github.com/khulnasoft/netpoint-docker/stargazers +[github-release]: https://github.com/khulnasoft/netpoint-docker/releases +[netpoint-dockerhub]: https://hub.docker.com/r/khulnasoft/netpoint/ +[netpoint-quayio]: https://quay.io/repository/khulnasoft/netpoint +[netpoint-ghcr]: https://github.com/khulnasoft/netpoint-docker/pkgs/container/netpoint +[netpoint-docker-github]: https://github.com/khulnasoft/netpoint-docker/ +[netpoint-docker-slack]: https://join.slack.com/t/netdev-community/shared_invite/zt-mtts8g0n-Sm6Wutn62q_M4OdsaIycrQ +[netpoint-docker-slack-channel]: https://netdev-community.slack.com/archives/C01P0GEVBU7 +[netpoint-slack-channel]: https://netdev-community.slack.com/archives/C01P0FRSXRV +[netpoint-docker-license]: https://github.com/khulnasoft/netpoint-docker/blob/release/LICENSE + +## Quickstart + +To get _NetPoint Docker_ up and running run the following commands. +There is a more complete [_Getting Started_ guide on our wiki][wiki-getting-started] which explains every step. + +```bash +git clone -b release https://github.com/khulnasoft/netpoint-docker.git +cd netpoint-docker +tee docker-compose.override.yml < We recommend to use either the `vX.Y.Z-a.b.c` tags or the `vX.Y-a.b.c` tags in production! + +* `vX.Y.Z-a.b.c`, `vX.Y-a.b.c`: + These are release builds containing _NetPoint version_ `vX.Y.Z`. + They contain the support files of _NetPoint Docker version_ `a.b.c`. + You must use _NetPoint Docker version_ `a.b.c` to guarantee the compatibility. + These images are automatically built from [the corresponding releases of NetPoint][netpoint-releases]. +* `latest-a.b.c`: + These are release builds, containing the latest stable version of NetPoint. + They contain the support files of _NetPoint Docker version_ `a.b.c`. + You must use _NetPoint Docker version_ `a.b.c` to guarantee the compatibility. + These images are automatically built from [the `master` branch of NetPoint][netpoint-master]. +* `snapshot-a.b.c`: + These are prerelease builds. + They contain the support files of _NetPoint Docker version_ `a.b.c`. + You must use _NetPoint Docker version_ `a.b.c` to guarantee the compatibility. + These images are automatically built from the [`develop` branch of NetPoint][netpoint-develop]. + +For each of the above tag, there is an extra tag: + +* `vX.Y.Z`, `vX.Y`: + This is the same version as `vX.Y.Z-a.b.c` (or `vX.Y-a.b.c`, respectively). + It always points to the latest version of _NetPoint Docker_. +* `latest` + This is the same version as `latest-a.b.c`. + It always points to the latest version of _NetPoint Docker_. +* `snapshot` + This is the same version as `snapshot-a.b.c`. + It always points to the latest version of _NetPoint Docker_. + +[netpoint-releases]: https://github.com/khulnasoft/netpoint/releases +[netpoint-master]: https://github.com/khulnasoft/netpoint/tree/master +[netpoint-develop]: https://github.com/khulnasoft/netpoint/tree/develop + +## Documentation + +Please refer [to our wiki on GitHub][netpoint-docker-wiki] for further information on how to use the NetPoint Docker image properly. +The wiki covers advanced topics such as using files for secrets, configuring TLS, deployment to Kubernetes, monitoring and configuring LDAP. + +Our wiki is a community effort. +Feel free to correct errors, update outdated information or provide additional guides and insights. + +[netpoint-docker-wiki]: https://github.com/khulnasoft/netpoint-docker/wiki/ + +## Getting Help + +Feel free to ask questions in our [GitHub Community][khulnasoft] +or [join our Slack][netpoint-docker-slack] and ask [in our channel `#netpoint-docker`][netpoint-docker-slack-channel], +which is free to use and where there are almost always people online that can help you in the Slack channel. + +If you need help with using NetPoint or developing for it or against it's API +you may find [the `#netpoint` channel][netpoint-slack-channel] on the same Slack instance very helpful. + +[khulnasoft]: https://github.com/khulnasoft/netpoint-docker/discussions + +## Dependencies + +This project relies only on _Docker_ and _docker-compose_ meeting these requirements: + +* The _Docker version_ must be at least `20.10.10`. +* The _containerd version_ must be at least `1.5.6`. +* The _docker-compose version_ must be at least `1.28.0`. + +To check the version installed on your system run `docker --version` and `docker compose version`. + +## Updating + +Please read [the release notes][releases] carefully when updating to a new image version. +Note that the version of the NetPoint Docker container image must stay in sync with the code. + +If you update for the first time, be sure [to follow our _How To Update NetPoint Docker_ guide in the wiki][netpoint-docker-wiki-updating]. + +[releases]: https://github.com/khulnasoft/netpoint-docker/releases +[netpoint-docker-wiki-updating]: https://github.com/khulnasoft/netpoint-docker/wiki/Updating + +## Rebuilding the Image + +`./build.sh` can be used to rebuild the container image. See `./build.sh --help` for more information. + +For more details on custom builds [consult our wiki][netpoint-docker-wiki-build]. + +[netpoint-docker-wiki-build]: https://github.com/khulnasoft/netpoint-docker/wiki/Build + +## Tests + +We have a test script. +It runs NetPoint's own unit tests and ensures that all initializers work: + +```bash +IMAGE=khulnasoft/netpoint:latest ./test.sh +``` + +## Support + +This repository is currently maintained by the community. +Please consider sponsoring the maintainers of this project. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..834f262 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.8.0 diff --git a/build-functions/check-commands.sh b/build-functions/check-commands.sh new file mode 100644 index 0000000..e998490 --- /dev/null +++ b/build-functions/check-commands.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +NEEDED_COMMANDS="curl jq docker skopeo" +for c in $NEEDED_COMMANDS; do + if ! command -v "$c" &>/dev/null; then + echo "⚠️ '$c' is not installed. Can't proceed with build." + exit 1 + fi +done diff --git a/build-functions/get-public-image-config.sh b/build-functions/get-public-image-config.sh new file mode 100644 index 0000000..0a19c3f --- /dev/null +++ b/build-functions/get-public-image-config.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +check_if_tags_exists() { + local image=$1 + local tag=$2 + skopeo list-tags "docker://$image" | jq -r ".Tags | contains([\"$tag\"])" +} + +get_image_label() { + local label=$1 + local image=$2 + skopeo inspect "docker://$image" | jq -r ".Labels[\"$label\"]" +} + +get_image_last_layer() { + local image=$1 + skopeo inspect "docker://$image" | jq -r ".Layers | last" +} diff --git a/build-functions/gh-functions.sh b/build-functions/gh-functions.sh new file mode 100644 index 0000000..4c04dc0 --- /dev/null +++ b/build-functions/gh-functions.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +### +# A regular echo, that only prints if ${GH_ACTION} is defined. +### +gh_echo() { + if [ -n "${GH_ACTION}" ]; then + echo "${@}" + fi +} + +### +# Prints the output to the file defined in ${GITHUB_ENV}. +# Only executes if ${GH_ACTION} is defined. +# Example Usage: gh_env "FOO_VAR=bar_value" +### +gh_env() { + if [ -n "${GH_ACTION}" ]; then + echo "${@}" >>"${GITHUB_ENV}" + fi +} + +### +# Prints the output to the file defined in ${GITHUB_OUTPUT}. +# Only executes if ${GH_ACTION} is defined. +# Example Usage: gh_env "FOO_VAR=bar_value" +### +gh_out() { + if [ -n "${GH_ACTION}" ]; then + echo "${@}" >>"$GITHUB_OUTPUT" + fi +} diff --git a/build-latest.sh b/build-latest.sh new file mode 100755 index 0000000..f31885f --- /dev/null +++ b/build-latest.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# Builds the latest released version + +# Check if we have everything needed for the build +source ./build-functions/check-commands.sh + +source ./build-functions/gh-functions.sh + +echo "▶️ $0 $*" + +CURL_ARGS=( + --silent +) + +### +# Checking for the presence of GITHUB_TOKEN +### +if [ -n "${GITHUB_TOKEN}" ]; then + echo "🗝 Performing authenticated Github API calls." + CURL_ARGS+=( + --header "Authorization: Bearer ${GITHUB_TOKEN}" + ) +else + echo "🕶 Performing unauthenticated Github API calls. This might result in lower Github rate limits!" +fi + +### +# Checking if PRERELEASE is either unset, 'true' or 'false' +### +if [ -n "${PRERELEASE}" ] && + { [ "${PRERELEASE}" != "true" ] && [ "${PRERELEASE}" != "false" ]; }; then + + if [ -z "${DEBUG}" ]; then + echo "⚠️ PRERELEASE must be either unset, 'true' or 'false', but was '${PRERELEASE}'!" + exit 1 + else + echo "⚠️ Would exit here with code '1', but DEBUG is enabled." + fi +fi + +### +# Calling Github to get the latest version +### +ORIGINAL_GITHUB_REPO="khulnasoft/netpoint" +GITHUB_REPO="${GITHUB_REPO-$ORIGINAL_GITHUB_REPO}" +URL_RELEASES="https://api.github.com/repos/${GITHUB_REPO}/releases" + +# Composing the JQ commans to extract the most recent version number +JQ_LATEST="group_by(.prerelease) | .[] | sort_by(.published_at) | reverse | .[0] | select(.prerelease==${PRERELEASE-false}) | .tag_name" + +CURL="curl" + +# Querying the Github API to fetch the most recent version number +VERSION=$($CURL "${CURL_ARGS[@]}" "${URL_RELEASES}" | jq -r "${JQ_LATEST}" 2>/dev/null) + +### +# Check if the prerelease version is actually higher than stable version +### +if [ "${PRERELEASE}" == "true" ]; then + JQ_STABLE="group_by(.prerelease) | .[] | sort_by(.published_at) | reverse | .[0] | select(.prerelease==false) | .tag_name" + STABLE_VERSION=$($CURL "${CURL_ARGS[@]}" "${URL_RELEASES}" | jq -r "${JQ_STABLE}" 2>/dev/null) + + MAJOR_STABLE=$(expr "${STABLE_VERSION}" : 'v\([0-9]\+\)') + MINOR_STABLE=$(expr "${STABLE_VERSION}" : 'v[0-9]\+\.\([0-9]\+\)') + MAJOR_UNSTABLE=$(expr "${VERSION}" : 'v\([0-9]\+\)') + MINOR_UNSTABLE=$(expr "${VERSION}" : 'v[0-9]\+\.\([0-9]\+\)') + + if { + [ "${MAJOR_STABLE}" -eq "${MAJOR_UNSTABLE}" ] && + [ "${MINOR_STABLE}" -ge "${MINOR_UNSTABLE}" ] + } || [ "${MAJOR_STABLE}" -gt "${MAJOR_UNSTABLE}" ]; then + + echo "❎ Latest unstable version '${VERSION}' is not higher than the latest stable version '$STABLE_VERSION'." + if [ -z "$DEBUG" ]; then + gh_out "skipped=true" + exit 0 + else + echo "⚠️ Would exit here with code '0', but DEBUG is enabled." + fi + fi +fi + +# shellcheck disable=SC2068 +./build.sh "${VERSION}" $@ +exit $? diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..becbbe2 --- /dev/null +++ b/build.sh @@ -0,0 +1,458 @@ +#!/bin/bash +# Clones the NetPoint repository with git from Github and builds the Dockerfile + +echo "▶️ $0 $*" + +set -e + +if [ "${1}x" == "x" ] || [ "${1}" == "--help" ] || [ "${1}" == "-h" ]; then + _BOLD=$(tput bold) + _GREEN=$(tput setaf 2) + _CYAN=$(tput setaf 6) + _CLEAR=$(tput sgr0) + + cat < [--push] + +branch The branch or tag to build. Required. +--push Pushes the built container image to the registry. + +${_BOLD}You can use the following ENV variables to customize the build:${_CLEAR} + +SRC_ORG Which fork of netpoint to use (i.e. github.com/\${SRC_ORG}/\${SRC_REPO}). + ${_GREEN}Default:${_CLEAR} khulnasoft + +SRC_REPO The name of the repository to use (i.e. github.com/\${SRC_ORG}/\${SRC_REPO}). + ${_GREEN}Default:${_CLEAR} netpoint + +URL Where to fetch the code from. + Must be a git repository. Can be private. + ${_GREEN}Default:${_CLEAR} https://github.com/\${SRC_ORG}/\${SRC_REPO}.git + +NETBOX_PATH The path where netpoint will be checkout out. + Must not be outside of the netpoint-docker repository (because of Docker)! + ${_GREEN}Default:${_CLEAR} .netpoint + +SKIP_GIT If defined, git is not invoked and \${NETBOX_PATH} will not be altered. + This may be useful, if you are manually managing the NETBOX_PATH. + ${_GREEN}Default:${_CLEAR} undefined + +TAG The version part of the image tag. + ${_GREEN}Default:${_CLEAR} + When =master: latest + When =develop: snapshot + Else: same as + +IMAGE_NAMES The names used for the image including the registry + Used for tagging the image. + ${_GREEN}Default:${_CLEAR} docker.io/khulnasoft/netpoint + ${_CYAN}Example:${_CLEAR} 'docker.io/khulnasoft/netpoint quay.io/khulnasoft/netpoint' + +DOCKER_TAG The name of the tag which is applied to the image. + Useful for pushing into another registry than hub.docker.com. + ${_GREEN}Default:${_CLEAR} \${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}:\${TAG} + +DOCKER_SHORT_TAG The name of the short tag which is applied to the + image. This is used to tag all patch releases to their + containing version e.g. v2.5.1 -> v2.5 + ${_GREEN}Default:${_CLEAR} \${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}:. + +DOCKERFILE The name of Dockerfile to use. + ${_GREEN}Default:${_CLEAR} Dockerfile + +DOCKER_FROM The base image to use. + ${_GREEN}Default:${_CLEAR} 'ubuntu:23.04' + +BUILDX_PLATFORMS + Specifies the platform(s) to build the image for. + ${_CYAN}Example:${_CLEAR} 'linux/amd64,linux/arm64' + ${_GREEN}Default:${_CLEAR} 'linux/amd64' + +BUILDX_BUILDER_NAME + If defined, the image build will be assigned to the given builder. + If you specify this variable, make sure that the builder exists. + If this value is not defined, a new builx builder with the directory name of the + current directory (i.e. '$(basename "${PWD}")') is created." + ${_CYAN}Example:${_CLEAR} 'clever_lovelace' + ${_GREEN}Default:${_CLEAR} undefined + +BUILDX_REMOVE_BUILDER + If defined (and only if BUILDX_BUILDER_NAME is undefined), + then the buildx builder created by this script will be removed after use. + This is useful if you build NetPoint Docker on an automated system that does + not manage the builders for you. + ${_CYAN}Example:${_CLEAR} 'on' + ${_GREEN}Default:${_CLEAR} undefined + +HTTP_PROXY The proxy to use for http requests. + ${_CYAN}Example:${_CLEAR} http://proxy.domain.tld:3128 + ${_GREEN}Default:${_CLEAR} undefined + +NO_PROXY Comma-separated list of domain extensions proxy should not be used for. + ${_CYAN}Example:${_CLEAR} .domain1.tld,.domain2.tld + ${_GREEN}Default:${_CLEAR} undefined + +DEBUG If defined, the script does not stop when certain checks are unsatisfied. + ${_GREEN}Default:${_CLEAR} undefined + +DRY_RUN Prints all build statements instead of running them. + ${_GREEN}Default:${_CLEAR} undefined + +GH_ACTION If defined, special 'echo' statements are enabled that set the + following environment variables in Github Actions: + - FINAL_DOCKER_TAG: The final value of the DOCKER_TAG env variable + ${_GREEN}Default:${_CLEAR} undefined + +${_BOLD}Examples:${_CLEAR} + +${0} master + This will fetch the latest 'master' branch, build a Docker Image and tag it + 'khulnasoft/netpoint:latest'. + +${0} develop + This will fetch the latest 'develop' branch, build a Docker Image and tag it + 'khulnasoft/netpoint:snapshot'. + +${0} v2.6.6 + This will fetch the 'v2.6.6' tag, build a Docker Image and tag it + 'khulnasoft/netpoint:v2.6.6' and 'khulnasoft/netpoint:v2.6'. + +${0} develop-2.7 + This will fetch the 'develop-2.7' branch, build a Docker Image and tag it + 'khulnasoft/netpoint:develop-2.7'. + +SRC_ORG=cimnine ${0} feature-x + This will fetch the 'feature-x' branch from https://github.com/cimnine/netpoint.git, + build a Docker Image and tag it 'khulnasoft/netpoint:feature-x'. + +SRC_ORG=cimnine DOCKER_ORG=cimnine ${0} feature-x + This will fetch the 'feature-x' branch from https://github.com/cimnine/netpoint.git, + build a Docker Image and tag it 'cimnine/netpoint:feature-x'. +END_OF_HELP + + if [ "${1}x" == "x" ]; then + exit 1 + else + exit 0 + fi +fi + +# Check if we have everything needed for the build +source ./build-functions/check-commands.sh +# Load all build functions +source ./build-functions/get-public-image-config.sh +source ./build-functions/gh-functions.sh + +IMAGE_NAMES="${IMAGE_NAMES-docker.io/khulnasoft/netpoint}" +IFS=' ' read -ra IMAGE_NAMES <<<"${IMAGE_NAMES}" + +### +# Enabling dry-run mode +### +if [ -z "${DRY_RUN}" ]; then + DRY="" +else + echo "⚠️ DRY_RUN MODE ON ⚠️" + DRY="echo" +fi + +gh_echo "::group::⤵️ Fetching the NetPoint source code" + +### +# Variables for fetching the NetPoint source +### +SRC_ORG="${SRC_ORG-khulnasoft}" +SRC_REPO="${SRC_REPO-netpoint}" +NETBOX_BRANCH="${1}" +URL="${URL-https://github.com/${SRC_ORG}/${SRC_REPO}.git}" +NETBOX_PATH="${NETBOX_PATH-.netpoint}" + +### +# Fetching the NetPoint source +### +if [ "${2}" != "--push-only" ] && [ -z "${SKIP_GIT}" ]; then + REMOTE_EXISTS=$(git ls-remote --heads --tags "${URL}" "${NETBOX_BRANCH}" | wc -l) + if [ "${REMOTE_EXISTS}" == "0" ]; then + echo "❌ Remote branch '${NETBOX_BRANCH}' not found in '${URL}'; Nothing to do" + gh_out "skipped=true" + exit 0 + fi + echo "🌐 Checking out '${NETBOX_BRANCH}' of NetPoint from the url '${URL}' into '${NETBOX_PATH}'" + if [ ! -d "${NETBOX_PATH}" ]; then + $DRY git clone -q --depth 10 -b "${NETBOX_BRANCH}" "${URL}" "${NETBOX_PATH}" + fi + + ( + $DRY cd "${NETBOX_PATH}" + # shellcheck disable=SC2030 + if [ -n "${HTTP_PROXY}" ]; then + git config http.proxy "${HTTP_PROXY}" + fi + + $DRY git remote set-url origin "${URL}" + $DRY git fetch -qp --depth 10 origin "${NETBOX_BRANCH}" + $DRY git checkout -qf FETCH_HEAD + $DRY git prune + ) + echo "✅ Checked out NetPoint" +fi + +gh_echo "::endgroup::" +gh_echo "::group::🧮 Calculating Values" + +### +# Determining the value for DOCKERFILE +# and checking whether it exists +### +DOCKERFILE="${DOCKERFILE-Dockerfile}" +if [ ! -f "${DOCKERFILE}" ]; then + echo "🚨 The Dockerfile ${DOCKERFILE} doesn't exist." + + if [ -z "${DEBUG}" ]; then + exit 1 + else + echo "⚠️ Would exit here with code '1', but DEBUG is enabled." + fi +fi + +### +# Determining the value for DOCKER_FROM +### +if [ -z "$DOCKER_FROM" ]; then + DOCKER_FROM="docker.io/ubuntu:23.04" +fi + +### +# Variables for labelling the docker image +### +BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M+00:00')" + +if [ -d ".git" ] && [ -z "${SKIP_GIT}" ]; then + GIT_REF="$(git rev-parse HEAD)" +fi + +# Read the project version from the `VERSION` file and trim it, see https://stackoverflow.com/a/3232433/172132 +PROJECT_VERSION="${PROJECT_VERSION-$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' VERSION)}" + +# Get the Git information from the netpoint directory +if [ -d "${NETBOX_PATH}/.git" ] && [ -z "${SKIP_GIT}" ]; then + NETBOX_GIT_REF=$( + cd "${NETBOX_PATH}" + git rev-parse HEAD + ) + NETBOX_GIT_BRANCH=$( + cd "${NETBOX_PATH}" + git rev-parse --abbrev-ref HEAD + ) + NETBOX_GIT_URL=$( + cd "${NETBOX_PATH}" + git remote get-url origin + ) +fi + +### +# Variables for tagging the docker image +### +DOCKER_REGISTRY="${DOCKER_REGISTRY-docker.io}" +DOCKER_ORG="${DOCKER_ORG-khulnasoft}" +DOCKER_REPO="${DOCKER_REPO-netpoint}" +case "${NETBOX_BRANCH}" in +master) + TAG="${TAG-latest}" + ;; +develop) + TAG="${TAG-snapshot}" + ;; +*) + TAG="${TAG-$NETBOX_BRANCH}" + ;; +esac + +### +# composing the final TARGET_DOCKER_TAG +### +TARGET_DOCKER_TAG="${DOCKER_TAG-${TAG}}" +TARGET_DOCKER_TAG_PROJECT="${TARGET_DOCKER_TAG}-${PROJECT_VERSION}" + +### +# composing the additional DOCKER_SHORT_TAG, +# i.e. "v2.6.1" becomes "v2.6", +# which is only relevant for version tags +# Also let "latest" follow the highest version +### +if [[ "${TAG}" =~ ^v([0-9]+)\.([0-9]+)\.[0-9]+$ ]]; then + MAJOR=${BASH_REMATCH[1]} + MINOR=${BASH_REMATCH[2]} + + TARGET_DOCKER_SHORT_TAG="${DOCKER_SHORT_TAG-v${MAJOR}.${MINOR}}" + TARGET_DOCKER_LATEST_TAG="latest" + TARGET_DOCKER_SHORT_TAG_PROJECT="${TARGET_DOCKER_SHORT_TAG}-${PROJECT_VERSION}" + TARGET_DOCKER_LATEST_TAG_PROJECT="${TARGET_DOCKER_LATEST_TAG}-${PROJECT_VERSION}" +fi + +IMAGE_NAME_TAGS=() +for IMAGE_NAME in "${IMAGE_NAMES[@]}"; do + IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_TAG}") + IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_TAG_PROJECT}") +done +if [ -n "${TARGET_DOCKER_SHORT_TAG}" ]; then + for IMAGE_NAME in "${IMAGE_NAMES[@]}"; do + IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_SHORT_TAG}") + IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_SHORT_TAG_PROJECT}") + IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_LATEST_TAG}") + IMAGE_NAME_TAGS+=("${IMAGE_NAME}:${TARGET_DOCKER_LATEST_TAG_PROJECT}") + done +fi + +FINAL_DOCKER_TAG="${IMAGE_NAME_TAGS[0]}" +gh_env "FINAL_DOCKER_TAG=${IMAGE_NAME_TAGS[0]}" + +### +# Checking if the build is necessary, +# meaning build only if one of those values changed: +# - a new tag is beeing created +# - base image digest +# - netpoint git ref (Label: netpoint.git-ref) +# - netpoint-docker git ref (Label: org.opencontainers.image.revision) +### +# Load information from registry (only for first registry in "IMAGE_NAMES") +SHOULD_BUILD="false" +BUILD_REASON="" +if [ -z "${GH_ACTION}" ]; then + # Asuming non Github builds should always proceed + SHOULD_BUILD="true" + BUILD_REASON="${BUILD_REASON} interactive" +elif [ "false" == "$(check_if_tags_exists "${IMAGE_NAMES[0]}" "$TARGET_DOCKER_TAG")" ]; then + SHOULD_BUILD="true" + BUILD_REASON="${BUILD_REASON} newtag" +else + echo "Checking labels for '${FINAL_DOCKER_TAG}'" + BASE_LAST_LAYER=$(get_image_last_layer "${DOCKER_FROM}") + OLD_BASE_LAST_LAYER=$(get_image_label netpoint.last-base-image-layer "${FINAL_DOCKER_TAG}") + NETBOX_GIT_REF_OLD=$(get_image_label netpoint.git-ref "${FINAL_DOCKER_TAG}") + GIT_REF_OLD=$(get_image_label org.opencontainers.image.revision "${FINAL_DOCKER_TAG}") + + if [ "${BASE_LAST_LAYER}" != "${OLD_BASE_LAST_LAYER}" ]; then + SHOULD_BUILD="true" + BUILD_REASON="${BUILD_REASON} ubuntu" + fi + if [ "${NETBOX_GIT_REF}" != "${NETBOX_GIT_REF_OLD}" ]; then + SHOULD_BUILD="true" + BUILD_REASON="${BUILD_REASON} netpoint" + fi + if [ "${GIT_REF}" != "${GIT_REF_OLD}" ]; then + SHOULD_BUILD="true" + BUILD_REASON="${BUILD_REASON} netpoint-docker" + fi +fi + +if [ "${SHOULD_BUILD}" != "true" ]; then + echo "Build skipped because sources didn't change" + gh_out "skipped=true" + exit 0 # Nothing to do -> exit +else + gh_out "skipped=false" +fi +gh_echo "::endgroup::" + +### +# Build the image +### +gh_echo "::group::🏗 Building the image" +### +# Composing all arguments for `docker build` +### +DOCKER_BUILD_ARGS=( + --pull + --target main + -f "${DOCKERFILE}" +) +for IMAGE_NAME in "${IMAGE_NAME_TAGS[@]}"; do + DOCKER_BUILD_ARGS+=(-t "${IMAGE_NAME}") +done + +# --label +DOCKER_BUILD_ARGS+=( + --label "netpoint.original-tag=${TARGET_DOCKER_TAG_PROJECT}" + --label "org.opencontainers.image.created=${BUILD_DATE}" + --label "org.opencontainers.image.version=${PROJECT_VERSION}" +) +if [ -d ".git" ] && [ -z "${SKIP_GIT}" ]; then + DOCKER_BUILD_ARGS+=( + --label "org.opencontainers.image.revision=${GIT_REF}" + ) +fi +if [ -d "${NETBOX_PATH}/.git" ] && [ -z "${SKIP_GIT}" ]; then + DOCKER_BUILD_ARGS+=( + --label "netpoint.git-branch=${NETBOX_GIT_BRANCH}" + --label "netpoint.git-ref=${NETBOX_GIT_REF}" + --label "netpoint.git-url=${NETBOX_GIT_URL}" + ) +fi +if [ -n "${BUILD_REASON}" ]; then + BUILD_REASON=$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<<"$BUILD_REASON") + DOCKER_BUILD_ARGS+=(--label "netpoint.build-reason=${BUILD_REASON}") + DOCKER_BUILD_ARGS+=(--label "netpoint.last-base-image-layer=${BASE_LAST_LAYER}") +fi + +# --build-arg +DOCKER_BUILD_ARGS+=(--build-arg "NETBOX_PATH=${NETBOX_PATH}") + +if [ -n "${DOCKER_FROM}" ]; then + DOCKER_BUILD_ARGS+=(--build-arg "FROM=${DOCKER_FROM}") +fi +# shellcheck disable=SC2031 +if [ -n "${HTTP_PROXY}" ]; then + DOCKER_BUILD_ARGS+=(--build-arg "http_proxy=${HTTP_PROXY}") + DOCKER_BUILD_ARGS+=(--build-arg "https_proxy=${HTTPS_PROXY}") +fi +if [ -n "${NO_PROXY}" ]; then + DOCKER_BUILD_ARGS+=(--build-arg "no_proxy=${NO_PROXY}") +fi + +DOCKER_BUILD_ARGS+=(--platform "${BUILDX_PLATFORM-linux/amd64}") +if [ "${2}" == "--push" ]; then + # output type=docker does not work with pushing + DOCKER_BUILD_ARGS+=( + --output=type=image + --push + ) +else + DOCKER_BUILD_ARGS+=( + --output=type=docker + ) +fi + +### +# Building the docker image +### +if [ -z "${BUILDX_BUILDER_NAME}" ]; then + BUILDX_BUILDER_NAME="$(basename "${PWD}")" +fi +if ! docker buildx ls | grep --quiet --word-regexp "${BUILDX_BUILDER_NAME}"; then + echo "👷 Creating new Buildx Builder '${BUILDX_BUILDER_NAME}'" + $DRY docker buildx create --name "${BUILDX_BUILDER_NAME}" + BUILDX_BUILDER_CREATED="yes" +fi + +echo "🐳 Building the Docker image '${TARGET_DOCKER_TAG_PROJECT}'." +echo " Build reason set to: ${BUILD_REASON}" +$DRY docker buildx \ + --builder "${BUILDX_BUILDER_NAME}" \ + build \ + "${DOCKER_BUILD_ARGS[@]}" \ + . +echo "✅ Finished building the Docker images" +gh_echo "::endgroup::" # End group for Build + +gh_echo "::group::🏗 Image Labels" +echo "🔎 Inspecting labels on '${IMAGE_NAME_TAGS[0]}'" +$DRY docker inspect "${IMAGE_NAME_TAGS[0]}" --format "{{json .Config.Labels}}" | jq +gh_echo "::endgroup::" + +gh_echo "::group::🏗 Clean up" +if [ -n "${BUILDX_REMOVE_BUILDER}" ] && [ "${BUILDX_BUILDER_CREATED}" == "yes" ]; then + echo "👷 Removing Buildx Builder '${BUILDX_BUILDER_NAME}'" + $DRY docker buildx rm "${BUILDX_BUILDER_NAME}" +fi +gh_echo "::endgroup::" diff --git a/configuration/configuration.py b/configuration/configuration.py new file mode 100644 index 0000000..3304765 --- /dev/null +++ b/configuration/configuration.py @@ -0,0 +1,318 @@ +#### +## We recommend to not edit this file. +## Create separate files to overwrite the settings. +## See `extra.py` as an example. +#### + +import re +from os import environ +from os.path import abspath, dirname, join +from typing import Any, Callable, Tuple + +# For reference see https://docs.netpoint.dev/en/stable/configuration/ +# Based on https://github.com/khulnasoft/netpoint/blob/develop/netpoint/netpoint/configuration_example.py + +### +# NetPoint-Docker Helper functions +### + +# Read secret from file +def _read_secret(secret_name: str, default: str | None = None) -> str | None: + try: + f = open('/run/secrets/' + secret_name, 'r', encoding='utf-8') + except EnvironmentError: + return default + else: + with f: + return f.readline().strip() + +# If the `map_fn` isn't defined, then the value that is read from the environment (or the default value if not found) is returned. +# If the `map_fn` is defined, then `map_fn` is invoked and the value (that was read from the environment or the default value if not found) +# is passed to it as a parameter. The value returned from `map_fn` is then the return value of this function. +# The `map_fn` is not invoked, if the value (that was read from the environment or the default value if not found) is None. +def _environ_get_and_map(variable_name: str, default: str | None = None, map_fn: Callable[[str], Any | None] = None) -> Any | None: + env_value = environ.get(variable_name, default) + + if env_value == None: + return env_value + + if not map_fn: + return env_value + + return map_fn(env_value) + +_AS_BOOL = lambda value : value.lower() == 'true' +_AS_INT = lambda value : int(value) +_AS_LIST = lambda value : list(filter(None, value.split(' '))) + +_BASE_DIR = dirname(dirname(abspath(__file__))) + +######################### +# # +# Required settings # +# # +######################### + +# This is a list of valid fully-qualified domain names (FQDNs) for the NetPoint server. NetPoint will not permit write +# access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name. +# +# Example: ALLOWED_HOSTS = ['netpoint.example.com', 'netpoint.internal.local'] +ALLOWED_HOSTS = environ.get('ALLOWED_HOSTS', '*').split(' ') +# ensure that '*' or 'localhost' is always in ALLOWED_HOSTS (needed for health checks) +if '*' not in ALLOWED_HOSTS and 'localhost' not in ALLOWED_HOSTS: + ALLOWED_HOSTS.append('localhost') + +# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters: +# https://docs.djangoproject.com/en/stable/ref/settings/#databases +DATABASE = { + 'NAME': environ.get('DB_NAME', 'netpoint'), # Database name + 'USER': environ.get('DB_USER', ''), # PostgreSQL username + 'PASSWORD': _read_secret('db_password', environ.get('DB_PASSWORD', '')), + # PostgreSQL password + 'HOST': environ.get('DB_HOST', 'localhost'), # Database server + 'PORT': environ.get('DB_PORT', ''), # Database port (leave blank for default) + 'OPTIONS': {'sslmode': environ.get('DB_SSLMODE', 'prefer')}, + # Database connection SSLMODE + 'CONN_MAX_AGE': _environ_get_and_map('DB_CONN_MAX_AGE', '300', _AS_INT), + # Max database connection age + 'DISABLE_SERVER_SIDE_CURSORS': _environ_get_and_map('DB_DISABLE_SERVER_SIDE_CURSORS', 'False', _AS_BOOL), + # Disable the use of server-side cursors transaction pooling +} + +# Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate +# configuration exists for each. Full connection details are required in both sections, and it is strongly recommended +# to use two separate database IDs. +REDIS = { + 'tasks': { + 'HOST': environ.get('REDIS_HOST', 'localhost'), + 'PORT': _environ_get_and_map('REDIS_PORT', 6379, _AS_INT), + 'USERNAME': environ.get('REDIS_USERNAME', ''), + 'PASSWORD': _read_secret('redis_password', environ.get('REDIS_PASSWORD', '')), + 'DATABASE': _environ_get_and_map('REDIS_DATABASE', 0, _AS_INT), + 'SSL': _environ_get_and_map('REDIS_SSL', 'False', _AS_BOOL), + 'INSECURE_SKIP_TLS_VERIFY': _environ_get_and_map('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False', _AS_BOOL), + }, + 'caching': { + 'HOST': environ.get('REDIS_CACHE_HOST', environ.get('REDIS_HOST', 'localhost')), + 'PORT': _environ_get_and_map('REDIS_CACHE_PORT', environ.get('REDIS_PORT', '6379'), _AS_INT), + 'USERNAME': environ.get('REDIS_CACHE_USERNAME', environ.get('REDIS_USERNAME', '')), + 'PASSWORD': _read_secret('redis_cache_password', environ.get('REDIS_CACHE_PASSWORD', environ.get('REDIS_PASSWORD', ''))), + 'DATABASE': _environ_get_and_map('REDIS_CACHE_DATABASE', '1', _AS_INT), + 'SSL': _environ_get_and_map('REDIS_CACHE_SSL', environ.get('REDIS_SSL', 'False'), _AS_BOOL), + 'INSECURE_SKIP_TLS_VERIFY': _environ_get_and_map('REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY', environ.get('REDIS_INSECURE_SKIP_TLS_VERIFY', 'False'), _AS_BOOL), + }, +} + +# This key is used for secure generation of random numbers and strings. It must never be exposed outside of this file. +# For optimal security, SECRET_KEY should be at least 50 characters in length and contain a mix of letters, numbers, and +# symbols. NetPoint will not run without this defined. For more information, see +# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECRET_KEY +SECRET_KEY = _read_secret('secret_key', environ.get('SECRET_KEY', '')) + + +######################### +# # +# Optional settings # +# # +######################### + +# # Specify one or more name and email address tuples representing NetPoint administrators. These people will be notified of +# # application errors (assuming correct email settings are provided). +# ADMINS = [ +# # ['John Doe', 'jdoe@example.com'], +# ] + +if 'ALLOWED_URL_SCHEMES' in environ: + ALLOWED_URL_SCHEMES = _environ_get_and_map('ALLOWED_URL_SCHEMES', None, _AS_LIST) + +# Optionally display a persistent banner at the top and/or bottom of every page. HTML is allowed. To display the same +# content in both banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP. +if 'BANNER_TOP' in environ: + BANNER_TOP = environ.get('BANNER_TOP', None) +if 'BANNER_BOTTOM' in environ: + BANNER_BOTTOM = environ.get('BANNER_BOTTOM', None) + +# Text to include on the login page above the login form. HTML is allowed. +if 'BANNER_LOGIN' in environ: + BANNER_LOGIN = environ.get('BANNER_LOGIN', None) + +# Maximum number of days to retain logged changes. Set to 0 to retain changes indefinitely. (Default: 90) +if 'CHANGELOG_RETENTION' in environ: + CHANGELOG_RETENTION = _environ_get_and_map('CHANGELOG_RETENTION', None, _AS_INT) + +# Maximum number of days to retain job results (scripts and reports). Set to 0 to retain job results in the database indefinitely. (Default: 90) +if 'JOB_RETENTION' in environ: + JOB_RETENTION = _environ_get_and_map('JOB_RETENTION', None, _AS_INT) +# JOBRESULT_RETENTION was renamed to JOB_RETENTION in the v3.5.0 release of NetPoint. For backwards compatibility, map JOBRESULT_RETENTION to JOB_RETENTION +elif 'JOBRESULT_RETENTION' in environ: + JOB_RETENTION = _environ_get_and_map('JOBRESULT_RETENTION', None, _AS_INT) + +# API Cross-Origin Resource Sharing (CORS) settings. If CORS_ORIGIN_ALLOW_ALL is set to True, all origins will be +# allowed. Otherwise, define a list of allowed origins using either CORS_ORIGIN_WHITELIST or +# CORS_ORIGIN_REGEX_WHITELIST. For more information, see https://github.com/ottoyiu/django-cors-headers +CORS_ORIGIN_ALLOW_ALL = _environ_get_and_map('CORS_ORIGIN_ALLOW_ALL', 'False', _AS_BOOL) +CORS_ORIGIN_WHITELIST = _environ_get_and_map('CORS_ORIGIN_WHITELIST', 'https://localhost', _AS_LIST) +CORS_ORIGIN_REGEX_WHITELIST = [re.compile(r) for r in _environ_get_and_map('CORS_ORIGIN_REGEX_WHITELIST', '', _AS_LIST)] + +# Set to True to enable server debugging. WARNING: Debugging introduces a substantial performance penalty and may reveal +# sensitive information about your installation. Only enable debugging while performing testing. +# Never enable debugging on a production system. +DEBUG = _environ_get_and_map('DEBUG', 'False', _AS_BOOL) + +# This parameter serves as a safeguard to prevent some potentially dangerous behavior, +# such as generating new database schema migrations. +# Set this to True only if you are actively developing the NetPoint code base. +DEVELOPER = _environ_get_and_map('DEVELOPER', 'False', _AS_BOOL) + +# Email settings +EMAIL = { + 'SERVER': environ.get('EMAIL_SERVER', 'localhost'), + 'PORT': _environ_get_and_map('EMAIL_PORT', 25, _AS_INT), + 'USERNAME': environ.get('EMAIL_USERNAME', ''), + 'PASSWORD': _read_secret('email_password', environ.get('EMAIL_PASSWORD', '')), + 'USE_SSL': _environ_get_and_map('EMAIL_USE_SSL', 'False', _AS_BOOL), + 'USE_TLS': _environ_get_and_map('EMAIL_USE_TLS', 'False', _AS_BOOL), + 'SSL_CERTFILE': environ.get('EMAIL_SSL_CERTFILE', ''), + 'SSL_KEYFILE': environ.get('EMAIL_SSL_KEYFILE', ''), + 'TIMEOUT': _environ_get_and_map('EMAIL_TIMEOUT', 10, _AS_INT), # seconds + 'FROM_EMAIL': environ.get('EMAIL_FROM', ''), +} + +# Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table +# (all prefixes and IP addresses not assigned to a VRF), set ENFORCE_GLOBAL_UNIQUE to True. +if 'ENFORCE_GLOBAL_UNIQUE' in environ: + ENFORCE_GLOBAL_UNIQUE = _environ_get_and_map('ENFORCE_GLOBAL_UNIQUE', None, _AS_BOOL) + +# Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and +# by anonymous users. List models in the form `.`. Add '*' to this list to exempt all models. +EXEMPT_VIEW_PERMISSIONS = _environ_get_and_map('EXEMPT_VIEW_PERMISSIONS', '', _AS_LIST) + +# HTTP proxies NetPoint should use when sending outbound HTTP requests (e.g. for webhooks). +# HTTP_PROXIES = { +# 'http': 'http://10.10.1.10:3128', +# 'https': 'http://10.10.1.10:1080', +# } + +# IP addresses recognized as internal to the system. The debugging toolbar will be available only to clients accessing +# NetPoint from an internal IP. +INTERNAL_IPS = _environ_get_and_map('INTERNAL_IPS', '127.0.0.1 ::1', _AS_LIST) + +# Enable GraphQL API. +if 'GRAPHQL_ENABLED' in environ: + GRAPHQL_ENABLED = _environ_get_and_map('GRAPHQL_ENABLED', None, _AS_BOOL) + +# # Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: +# # https://docs.djangoproject.com/en/stable/topics/logging/ +# LOGGING = {} + +# Automatically reset the lifetime of a valid session upon each authenticated request. Enables users to remain +# authenticated to NetPoint indefinitely. +LOGIN_PERSISTENCE = _environ_get_and_map('LOGIN_PERSISTENCE', 'False', _AS_BOOL) + +# Setting this to True will permit only authenticated users to access any part of NetPoint. By default, anonymous users +# are permitted to access most data in NetPoint (excluding secrets) but not make any changes. +LOGIN_REQUIRED = _environ_get_and_map('LOGIN_REQUIRED', 'False', _AS_BOOL) + +# The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to +# re-authenticate. (Default: 1209600 [14 days]) +LOGIN_TIMEOUT = _environ_get_and_map('LOGIN_TIMEOUT', 1209600, _AS_INT) + +# Setting this to True will display a "maintenance mode" banner at the top of every page. +if 'MAINTENANCE_MODE' in environ: + MAINTENANCE_MODE = _environ_get_and_map('MAINTENANCE_MODE', None, _AS_BOOL) + +# Maps provider +if 'MAPS_URL' in environ: + MAPS_URL = environ.get('MAPS_URL', None) + +# An API consumer can request an arbitrary number of objects =by appending the "limit" parameter to the URL (e.g. +# "?limit=1000"). This setting defines the maximum limit. Setting it to 0 or None will allow an API consumer to request +# all objects by specifying "?limit=0". +if 'MAX_PAGE_SIZE' in environ: + MAX_PAGE_SIZE = _environ_get_and_map('MAX_PAGE_SIZE', None, _AS_INT) + +# The file path where uploaded media such as image attachments are stored. A trailing slash is not needed. Note that +# the default value of this setting is derived from the installed location. +MEDIA_ROOT = environ.get('MEDIA_ROOT', join(_BASE_DIR, 'media')) + +# Expose Prometheus monitoring metrics at the HTTP endpoint '/metrics' +METRICS_ENABLED = _environ_get_and_map('METRICS_ENABLED', 'False', _AS_BOOL) + +# Determine how many objects to display per page within a list. (Default: 50) +if 'PAGINATE_COUNT' in environ: + PAGINATE_COUNT = _environ_get_and_map('PAGINATE_COUNT', None, _AS_INT) + +# # Enable installed plugins. Add the name of each plugin to the list. +# PLUGINS = [] + +# # Plugins configuration settings. These settings are used by various plugins that the user may have installed. +# # Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings. +# PLUGINS_CONFIG = { +# } + +# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to +# prefer IPv4 instead. +if 'PREFER_IPV4' in environ: + PREFER_IPV4 = _environ_get_and_map('PREFER_IPV4', None, _AS_BOOL) + +# The default value for the amperage field when creating new power feeds. +if 'POWERFEED_DEFAULT_AMPERAGE' in environ: + POWERFEED_DEFAULT_AMPERAGE = _environ_get_and_map('POWERFEED_DEFAULT_AMPERAGE', None, _AS_INT) + +# The default value (percentage) for the max_utilization field when creating new power feeds. +if 'POWERFEED_DEFAULT_MAX_UTILIZATION' in environ: + POWERFEED_DEFAULT_MAX_UTILIZATION = _environ_get_and_map('POWERFEED_DEFAULT_MAX_UTILIZATION', None, _AS_INT) + +# The default value for the voltage field when creating new power feeds. +if 'POWERFEED_DEFAULT_VOLTAGE' in environ: + POWERFEED_DEFAULT_VOLTAGE = _environ_get_and_map('POWERFEED_DEFAULT_VOLTAGE', None, _AS_INT) + +# Rack elevation size defaults, in pixels. For best results, the ratio of width to height should be roughly 10:1. +if 'RACK_ELEVATION_DEFAULT_UNIT_HEIGHT' in environ: + RACK_ELEVATION_DEFAULT_UNIT_HEIGHT = _environ_get_and_map('RACK_ELEVATION_DEFAULT_UNIT_HEIGHT', None, _AS_INT) +if 'RACK_ELEVATION_DEFAULT_UNIT_WIDTH' in environ: + RACK_ELEVATION_DEFAULT_UNIT_WIDTH = _environ_get_and_map('RACK_ELEVATION_DEFAULT_UNIT_WIDTH', None, _AS_INT) + +# Remote authentication support +REMOTE_AUTH_ENABLED = _environ_get_and_map('REMOTE_AUTH_ENABLED', 'False', _AS_BOOL) +REMOTE_AUTH_BACKEND = _environ_get_and_map('REMOTE_AUTH_BACKEND', 'netpoint.authentication.RemoteUserBackend', _AS_LIST) +REMOTE_AUTH_HEADER = environ.get('REMOTE_AUTH_HEADER', 'HTTP_REMOTE_USER') +REMOTE_AUTH_AUTO_CREATE_USER = _environ_get_and_map('REMOTE_AUTH_AUTO_CREATE_USER', 'False', _AS_BOOL) +REMOTE_AUTH_DEFAULT_GROUPS = _environ_get_and_map('REMOTE_AUTH_DEFAULT_GROUPS', '', _AS_LIST) +# REMOTE_AUTH_DEFAULT_PERMISSIONS = {} + +# This repository is used to check whether there is a new release of NetPoint available. Set to None to disable the +# version check or use the URL below to check for release in the official NetPoint repository. +RELEASE_CHECK_URL = environ.get('RELEASE_CHECK_URL', None) +# RELEASE_CHECK_URL = 'https://api.github.com/repos/khulnasoft/netpoint/releases' + +# Maximum execution time for background tasks, in seconds. +RQ_DEFAULT_TIMEOUT = _environ_get_and_map('RQ_DEFAULT_TIMEOUT', 300, _AS_INT) + +# The name to use for the csrf token cookie. +CSRF_COOKIE_NAME = environ.get('CSRF_COOKIE_NAME', 'csrftoken') + +# Cross-Site-Request-Forgery-Attack settings. If Netbox is sitting behind a reverse proxy, you might need to set the CSRF_TRUSTED_ORIGINS flag. +# Django 4.0 requires to specify the URL Scheme in this setting. An example environment variable could be specified like: +# CSRF_TRUSTED_ORIGINS=https://demo.netpoint.dev http://demo.netpoint.dev +CSRF_TRUSTED_ORIGINS = _environ_get_and_map('CSRF_TRUSTED_ORIGINS', '', _AS_LIST) + +# The name to use for the session cookie. +SESSION_COOKIE_NAME = environ.get('SESSION_COOKIE_NAME', 'sessionid') + +# By default, NetPoint will store session data in the database. Alternatively, a file path can be specified here to use +# local file storage instead. (This can be useful for enabling authentication on a standby instance with read-only +# database access.) Note that the user as which NetPoint runs must have read and write permissions to this path. +SESSION_FILE_PATH = environ.get('SESSION_FILE_PATH', environ.get('SESSIONS_ROOT', None)) + +# Time zone (default: UTC) +TIME_ZONE = environ.get('TIME_ZONE', 'UTC') + +# Date/time formatting. See the following link for supported formats: +# https://docs.djangoproject.com/en/stable/ref/templates/builtins/#date +DATE_FORMAT = environ.get('DATE_FORMAT', 'N j, Y') +SHORT_DATE_FORMAT = environ.get('SHORT_DATE_FORMAT', 'Y-m-d') +TIME_FORMAT = environ.get('TIME_FORMAT', 'g:i a') +SHORT_TIME_FORMAT = environ.get('SHORT_TIME_FORMAT', 'H:i:s') +DATETIME_FORMAT = environ.get('DATETIME_FORMAT', 'N j, Y g:i a') +SHORT_DATETIME_FORMAT = environ.get('SHORT_DATETIME_FORMAT', 'Y-m-d H:i') diff --git a/configuration/extra.py b/configuration/extra.py new file mode 100644 index 0000000..f57cf46 --- /dev/null +++ b/configuration/extra.py @@ -0,0 +1,49 @@ +#### +## This file contains extra configuration options that can't be configured +## directly through environment variables. +#### + +## Specify one or more name and email address tuples representing NetPoint administrators. These people will be notified of +## application errors (assuming correct email settings are provided). +# ADMINS = [ +# # ['John Doe', 'jdoe@example.com'], +# ] + + +## URL schemes that are allowed within links in NetPoint +# ALLOWED_URL_SCHEMES = ( +# 'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp', +# ) + +## Enable installed plugins. Add the name of each plugin to the list. +# from netpoint.configuration.configuration import PLUGINS +# PLUGINS.append('my_plugin') + +## Plugins configuration settings. These settings are used by various plugins that the user may have installed. +## Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings. +# from netpoint.configuration.configuration import PLUGINS_CONFIG +# PLUGINS_CONFIG['my_plugin'] = { +# 'foo': 'bar', +# 'buzz': 'bazz' +# } + + +## Remote authentication support +# REMOTE_AUTH_DEFAULT_PERMISSIONS = {} + + +## By default uploaded media is stored on the local filesystem. Using Django-storages is also supported. Provide the +## class path of the storage driver in STORAGE_BACKEND and any configuration options in STORAGE_CONFIG. For example: +# STORAGE_BACKEND = 'storages.backends.s3boto3.S3Boto3Storage' +# STORAGE_CONFIG = { +# 'AWS_ACCESS_KEY_ID': 'Key ID', +# 'AWS_SECRET_ACCESS_KEY': 'Secret', +# 'AWS_STORAGE_BUCKET_NAME': 'netpoint', +# 'AWS_S3_REGION_NAME': 'eu-west-1', +# } + + +## This file can contain arbitrary Python code, e.g.: +# from datetime import datetime +# now = datetime.now().strftime("%d/%m/%Y %H:%M:%S") +# BANNER_TOP = f'This instance started on {now}.' diff --git a/configuration/logging.py b/configuration/logging.py new file mode 100644 index 0000000..10e195a --- /dev/null +++ b/configuration/logging.py @@ -0,0 +1,55 @@ +# # Remove first comment(#) on each line to implement this working logging example. +# # Add LOGLEVEL environment variable to netpoint if you use this example & want a different log level. +# from os import environ + +# # Set LOGLEVEL in netpoint.env or docker-compose.overide.yml to override a logging level of INFO. +# LOGLEVEL = environ.get('LOGLEVEL', 'INFO') + +# LOGGING = { + +# 'version': 1, +# 'disable_existing_loggers': False, +# 'formatters': { +# 'verbose': { +# 'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}', +# 'style': '{', +# }, +# 'simple': { +# 'format': '{levelname} {message}', +# 'style': '{', +# }, +# }, +# 'filters': { +# 'require_debug_false': { +# '()': 'django.utils.log.RequireDebugFalse', +# }, +# }, +# 'handlers': { +# 'console': { +# 'level': LOGLEVEL, +# 'filters': ['require_debug_false'], +# 'class': 'logging.StreamHandler', +# 'formatter': 'simple' +# }, +# 'mail_admins': { +# 'level': 'ERROR', +# 'class': 'django.utils.log.AdminEmailHandler', +# 'filters': ['require_debug_false'] +# } +# }, +# 'loggers': { +# 'django': { +# 'handlers': ['console'], +# 'propagate': True, +# }, +# 'django.request': { +# 'handlers': ['mail_admins'], +# 'level': 'ERROR', +# 'propagate': False, +# }, +# 'django_auth_ldap': { +# 'handlers': ['console',], +# 'level': LOGLEVEL, +# } +# } +# } diff --git a/configuration/plugins.py b/configuration/plugins.py new file mode 100644 index 0000000..1469920 --- /dev/null +++ b/configuration/plugins.py @@ -0,0 +1,13 @@ +# Add your plugins and plugin settings here. +# Of course uncomment this file out. + +# To learn how to build images with your required plugins +# See https://github.com/khulnasoft/netpoint-docker/wiki/Using-Netbox-Plugins + +# PLUGINS = ["netpoint_bgp"] + +# PLUGINS_CONFIG = { +# "netpoint_bgp": { +# ADD YOUR SETTINGS HERE +# } +# } diff --git a/docker-compose.override.yml.example b/docker-compose.override.yml.example new file mode 100644 index 0000000..5ae2fc3 --- /dev/null +++ b/docker-compose.override.yml.example @@ -0,0 +1,23 @@ +version: '3.4' +services: + netpoint: + ports: + - "8000:8080" + # If you want the Nginx unit status page visible from the + # outside of the container add the following port mapping: + # - "8001:8081" + # healthcheck: + # Time for which the health check can fail after the container is started. + # This depends mostly on the performance of your database. On the first start, + # when all tables need to be created the start_period should be higher than on + # subsequent starts. For the first start after major version upgrades of NetPoint + # the start_period might also need to be set higher. + # Default value in our docker-compose.yml is 60s + # start_period: 90s + # environment: + # SKIP_SUPERUSER: "false" + # SUPERUSER_API_TOKEN: "" + # SUPERUSER_EMAIL: "" + # SUPERUSER_NAME: "" + # SUPERUSER_PASSWORD: "" + diff --git a/docker-compose.test.override.yml b/docker-compose.test.override.yml new file mode 100644 index 0000000..223c818 --- /dev/null +++ b/docker-compose.test.override.yml @@ -0,0 +1,6 @@ +version: '3.4' +services: + netpoint: + ports: + - "127.0.0.1:8000:8080" + diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 0000000..4849510 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,66 @@ +version: '3.4' +services: + netpoint: &netpoint + image: ${IMAGE-khulnasoft/netpoint:latest} + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + redis-cache: + condition: service_healthy + env_file: env/netpoint.env + user: 'unit:root' + volumes: + - ./test-configuration/test_config.py:/etc/netpoint/config/test_config.py:z,ro + healthcheck: + start_period: ${NETBOX_START_PERIOD-120s} + timeout: 3s + interval: 15s + test: "curl -f http://localhost:8080/api/ || exit 1" + netpoint-worker: + <<: *netpoint + command: + - /opt/netpoint/venv/bin/python + - /opt/netpoint/netpoint/manage.py + - rqworker + healthcheck: + start_period: 40s + timeout: 3s + interval: 15s + test: "ps -aux | grep -v grep | grep -q rqworker || exit 1" + netpoint-housekeeping: + <<: *netpoint + command: + - /opt/netpoint/housekeeping.sh + healthcheck: + start_period: 40s + timeout: 3s + interval: 15s + test: "ps -aux | grep -v grep | grep -q housekeeping || exit 1" + postgres: + image: postgres:16-alpine + env_file: env/postgres.env + healthcheck: + test: "pg_isready -t 2 -d $$POSTGRES_DB -U $$POSTGRES_USER" ## $$ because of docker-compose + interval: 10s + timeout: 5s + retries: 5 + redis: &redis + image: redis:7-alpine + command: + - sh + - -c # this is to evaluate the $REDIS_PASSWORD from the env + - redis-server --appendonly yes --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose + env_file: env/redis.env + healthcheck: + start_period: 20s + timeout: 3s + interval: 15s + test: "timeout 2 redis-cli ping" + redis-cache: + <<: *redis + env_file: env/redis-cache.env +volumes: + netpoint-media-files: + driver: local diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..61f3322 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,87 @@ +version: '3.4' +services: + netpoint: &netpoint + image: docker.io/khulnasoft/netpoint:${VERSION-v3.7-2.8.0} + depends_on: + - postgres + - redis + - redis-cache + env_file: env/netpoint.env + user: 'unit:root' + healthcheck: + start_period: 60s + timeout: 3s + interval: 15s + test: "curl -f http://localhost:8080/api/ || exit 1" + volumes: + - ./configuration:/etc/netpoint/config:z,ro + - netpoint-media-files:/opt/netpoint/netpoint/media:rw + - netpoint-reports-files:/opt/netpoint/netpoint/reports:rw + - netpoint-scripts-files:/opt/netpoint/netpoint/scripts:rw + netpoint-worker: + <<: *netpoint + depends_on: + netpoint: + condition: service_healthy + command: + - /opt/netpoint/venv/bin/python + - /opt/netpoint/netpoint/manage.py + - rqworker + healthcheck: + start_period: 20s + timeout: 3s + interval: 15s + test: "ps -aux | grep -v grep | grep -q rqworker || exit 1" + netpoint-housekeeping: + <<: *netpoint + depends_on: + netpoint: + condition: service_healthy + command: + - /opt/netpoint/housekeeping.sh + healthcheck: + start_period: 20s + timeout: 3s + interval: 15s + test: "ps -aux | grep -v grep | grep -q housekeeping || exit 1" + + # postgres + postgres: + image: docker.io/postgres:16-alpine + env_file: env/postgres.env + volumes: + - netpoint-postgres-data:/var/lib/postgresql/data + + # redis + redis: + image: docker.io/redis:7-alpine + command: + - sh + - -c # this is to evaluate the $REDIS_PASSWORD from the env + - redis-server --appendonly yes --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose + env_file: env/redis.env + volumes: + - netpoint-redis-data:/data + redis-cache: + image: docker.io/redis:7-alpine + command: + - sh + - -c # this is to evaluate the $REDIS_PASSWORD from the env + - redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose + env_file: env/redis-cache.env + volumes: + - netpoint-redis-cache-data:/data + +volumes: + netpoint-media-files: + driver: local + netpoint-postgres-data: + driver: local + netpoint-redis-cache-data: + driver: local + netpoint-redis-data: + driver: local + netpoint-reports-files: + driver: local + netpoint-scripts-files: + driver: local diff --git a/docker/configuration.docker.py b/docker/configuration.docker.py new file mode 100644 index 0000000..1b2e88b --- /dev/null +++ b/docker/configuration.docker.py @@ -0,0 +1,91 @@ +## Generic Parts +# These functions are providing the functionality to load +# arbitrary configuration files. +# +# They can be imported by other code (see `ldap_config.py` for an example). + +import importlib.util +import sys +from os import scandir +from os.path import abspath, isfile + + +def _filename(f): + return f.name + + +def _import(module_name, path, loaded_configurations): + spec = importlib.util.spec_from_file_location("", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + sys.modules[module_name] = module + + loaded_configurations.insert(0, module) + + print(f"🧬 loaded config '{path}'") + + +def read_configurations(config_module, config_dir, main_config): + loaded_configurations = [] + + main_config_path = abspath(f"{config_dir}/{main_config}.py") + if isfile(main_config_path): + _import(f"{config_module}.{main_config}", main_config_path, loaded_configurations) + else: + print(f"⚠️ Main configuration '{main_config_path}' not found.") + + with scandir(config_dir) as it: + for f in sorted(it, key=_filename): + if not f.is_file(): + continue + + if f.name.startswith("__"): + continue + + if not f.name.endswith(".py"): + continue + + if f.name == f"{main_config}.py": + continue + + if f.name == f"{config_dir}.py": + continue + + module_name = f"{config_module}.{f.name[:-len('.py')]}".replace(".", "_") + _import(module_name, f.path, loaded_configurations) + + if len(loaded_configurations) == 0: + print(f"‼️ No configuration files found in '{config_dir}'.") + raise ImportError(f"No configuration files found in '{config_dir}'.") + + return loaded_configurations + + +## Specific Parts +# This section's code actually loads the various configuration files +# into the module with the given name. +# It contains the logic to resolve arbitrary configuration options by +# levaraging dynamic programming using `__getattr__`. + + +_loaded_configurations = read_configurations( + config_dir="/etc/netpoint/config/", + config_module="netpoint.configuration", + main_config="configuration", +) + + +def __getattr__(name): + for config in _loaded_configurations: + try: + return getattr(config, name) + except: + pass + raise AttributeError + + +def __dir__(): + names = [] + for config in _loaded_configurations: + names.extend(config.__dir__()) + return names diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh new file mode 100755 index 0000000..8427605 --- /dev/null +++ b/docker/docker-entrypoint.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# Runs on every start of the NetPoint Docker container + +# Stop when an error occures +set -e + +# Allows NetPoint to be run as non-root users +umask 002 + +# Load correct Python3 env +# shellcheck disable=SC1091 +source /opt/netpoint/venv/bin/activate + +# Try to connect to the DB +DB_WAIT_TIMEOUT=${DB_WAIT_TIMEOUT-3} +MAX_DB_WAIT_TIME=${MAX_DB_WAIT_TIME-30} +CUR_DB_WAIT_TIME=0 +while [ "${CUR_DB_WAIT_TIME}" -lt "${MAX_DB_WAIT_TIME}" ]; do + # Read and truncate connection error tracebacks to last line by default + exec {psfd}< <(./manage.py showmigrations 2>&1) + read -rd '' DB_ERR <&$psfd || : + exec {psfd}<&- + wait $! && break + if [ -n "$DB_WAIT_DEBUG" ]; then + echo "$DB_ERR" + else + readarray -tn 0 DB_ERR_LINES <<<"$DB_ERR" + echo "${DB_ERR_LINES[@]: -1}" + echo "[ Use DB_WAIT_DEBUG=1 in netpoint.env to print full traceback for errors here ]" + fi + echo "⏳ Waiting on DB... (${CUR_DB_WAIT_TIME}s / ${MAX_DB_WAIT_TIME}s)" + sleep "${DB_WAIT_TIMEOUT}" + CUR_DB_WAIT_TIME=$((CUR_DB_WAIT_TIME + DB_WAIT_TIMEOUT)) +done +if [ "${CUR_DB_WAIT_TIME}" -ge "${MAX_DB_WAIT_TIME}" ]; then + echo "❌ Waited ${MAX_DB_WAIT_TIME}s or more for the DB to become ready." + exit 1 +fi +# Check if update is needed +if ! ./manage.py migrate --check >/dev/null 2>&1; then + echo "⚙️ Applying database migrations" + ./manage.py migrate --no-input + echo "⚙️ Running trace_paths" + ./manage.py trace_paths --no-input + echo "⚙️ Removing stale content types" + ./manage.py remove_stale_contenttypes --no-input + echo "⚙️ Removing expired user sessions" + ./manage.py clearsessions + echo "⚙️ Building search index (lazy)" + ./manage.py reindex --lazy +fi + +# Create Superuser if required +if [ "$SKIP_SUPERUSER" == "true" ]; then + echo "↩️ Skip creating the superuser" +else + if [ -z ${SUPERUSER_NAME+x} ]; then + SUPERUSER_NAME='admin' + fi + if [ -z ${SUPERUSER_EMAIL+x} ]; then + SUPERUSER_EMAIL='admin@example.com' + fi + if [ -f "/run/secrets/superuser_password" ]; then + SUPERUSER_PASSWORD="$(" "${@}" +} + +# check errors shall exit with code 1 + +check_clean_repo() { + changes=$(git status --porcelain 2>/dev/null) + if [ ${?} ] && [ -n "$changes" ]; then + echo_nok "There are git changes pending:" + echo "$changes" + echo_hint "Please clean the repository before continueing: git stash --include-untracked" + exit 1 + fi + echo_ok "Repository has no pending changes." +} + +check_branch() { + expected_branch="${1}" + actual_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) + if [ ${?} ] && [ "${actual_branch}" != "${expected_branch}" ]; then + echo_nok "Current branch should be '${expected_branch}', but is '${actual_branch}'." + echo_hint "Please change to the '${expected_branch}' branch: git checkout ${expected_branch}" + exit 1 + fi + echo_ok "The current branch is '${actual_branch}'." +} + +check_upstream() { + expected_upstream_branch="origin/${1}" + actual_upstream_branch=$(git rev-parse --abbrev-ref '@{upstream}' 2>/dev/null) + if [ ${?} ] && [ "${actual_upstream_branch}" != "${expected_upstream_branch}" ]; then + echo_nok "Current upstream branch should be '${expected_upstream_branch}', but is '${actual_upstream_branch}'." + echo_hint "Please set '${expected_upstream_branch}' as the upstream branch: git branch --set-upstream-to=${expected_upstream_branch}" + exit 1 + fi + echo_ok "The current upstream branch is '${actual_upstream_branch}'." +} + +check_origin() { + expected_origin="git@github.com:${REPO}.git" + actual_origin=$(git remote get-url origin 2>/dev/null) + if [ ${?} ] && [ "${actual_origin}" != "${expected_origin}" ]; then + echo_nok "The url of origin is '${actual_origin}', but '${expected_origin}' is expected." + echo_hint "Please set '${expected_origin}' as the url for origin: git origin set-url '${expected_origin}'" + exit 1 + fi + echo_ok "The current origin url is '${actual_origin}'." +} + +check_latest() { + git fetch --tags origin + + local_head_commit=$(git rev-parse HEAD 2>/dev/null) + remote_head_commit=$(git rev-parse FETCH_HEAD 2>/dev/null) + if [ "${local_head_commit}" != "${remote_head_commit}" ]; then + echo_nok "HEAD is at '${local_head_commit}', but FETCH_HEAD is at '${remote_head_commit}'." + echo_hint "Please ensure that you have pushed and pulled all the latest chanegs: git pull --prune --rebase origin; git push origin" + exit 1 + fi + echo_ok "HEAD and FETCH_HEAD both point to '${local_head_commit}'." +} + +check_tag() { + local tag + + tag=$(/dev/null >/dev/null; then + echo_nok "The tag '${tag}' already points to '$(git rev-parse "${tag}" 2>/dev/null)'." + echo_hint "Please ensure that the 'VERSION' file has been updated before trying to release: echo X.Y.Z > VERSION" + exit 1 + fi + echo_ok "The tag '${tag}' does not exist yet." +} + +check_develop() { + echomoji 📋 "?" "Checking 'develop' branch" + + check_branch develop + check_upstream develop + check_clean_repo + check_latest +} + +check_release() { + echomoji 📋 "?" "Checking 'release' branch" + + check_upstream release + check_clean_repo + check_latest +} + +# git errors shall exit with code 2 + +git_switch() { + echomoji 🔀 "≈" "Switching to '${1}' branch…" + if ! git checkout "${1}" >/dev/null; then + echo_nok "It was not possible to switch to the branch '${1}'." + exit 2 + fi + echo_ok "The branch is now '${1}'." +} + +git_tag() { + echomoji 🏷 "X" "Tagging version '${1}'…" + if ! git tag "${1}"; then + echo_nok "The tag '${1}' was not created because of an error." + exit 2 + fi + echo_ok "The tag '$(