From 736a84ac359aef91b1eb2dadaffc157d93f4377b Mon Sep 17 00:00:00 2001 From: Vikash Singh <3116482+vi3k6i5@users.noreply.github.com> Date: Thu, 16 Sep 2021 14:46:18 +0530 Subject: [PATCH] chor: updating stable/3.2.x branch with latest main commits (#709) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: fix release build (#659) * chore: fix release build Fix release build by migrating to secret manager secrets and use templated kokoro configs for docs/ and release/ * fix: fix config names * chore: add populate secrets script * docs: fix license * chore: preserve original year * chore: revert years in manually committed files * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/master/packages/owl-bot/README.md * chore: update lockfile * chore: add .kokoro/docker directory Co-authored-by: Owl Bot * feat: Added support for check constraint (#679) * feat: Added support for check constraint * fix: change decimal out of scale ProgramingError to ValueError * fix: skip check_constraints tests when running on emmulator * fix: remove check constraint for emulator * docs: update docs to show decimal field support and check constraints but no support for unsigned data type (#683) * test: Performance Testing (#675) * performance files * test_benchmark * performance testing changes * changes in benchmark performance for prod * changes to number of runs * adding comments * linting changes * changes for 3.2 * Revert "changes for 3.2" This reverts commit 488035c0989595c09f7215c5a03ec6fd42555bda. * adding licence * chore: release 2.2.1b2 (#685) Release-As: 2.2.1b2 * chore: release 2.2.1b2 (#687) * chore: release 2.2.1b2 * Updated CHANGELOG.md Corrected the change log msg. Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> Co-authored-by: Vikash Singh <3116482+vi3k6i5@users.noreply.github.com> * fix: Bump version number after 2.2.1b2 release (#688) * chor: Update repo to say beta release instead of alpha (#691) * fix: Bump version number after 2.2.1b2 release * Update setup.py Current release is beta so updating the same in setup.py * chore: release 2.2.1b3 (#693) Release-As: 2.2.1b3 * chore: release 2.2.1b3 (#694) * chore: release 2.2.1b3 * Updated CHANGELOG.md Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> Co-authored-by: Vikash Singh <3116482+vi3k6i5@users.noreply.github.com> * fix: Bump version number after 2.2.1b3 release (#696) * chore: release 2.2.1b3 Release-As: 2.2.1b3 * fix: Bump version number after 2.2.1b3 release * fix: add test samples script and related kokoro files * fix: correct repo name from python-spanner to python-spanner-django * fix: correct license from Apache to BSD style * docs: lint fix for samples (#697) * Docs: fix changelog link and sample examples. (#700) * docs: update docs to show decimal field support and check constraints but no support for unsigned data type * docs: linked changelog correctly * docs: fix doc links for sample examples * fix: skip test cursor_executemany_with_empty_params_list as spanner support is not there * docs: update dbapi location in overview asset file (#702) * chore: migrate to main branch (#706) * chore: migrate to main branch * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * use latest post processor image * remove obsolete replacements in owlbot.py * update post processor image Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou Co-authored-by: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Co-authored-by: Owl Bot Co-authored-by: Astha Mohta <35952883+asthamohta@users.noreply.github.com> Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> Co-authored-by: Dan Lee <71398022+dandhlee@users.noreply.github.com> Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 9 + .github/.OwlBot.yaml | 8 + .../django_tests_against_emulator0.yml | 2 +- .../django_tests_against_emulator1.yml | 2 +- .../django_tests_against_emulator2.yml | 2 +- .../django_tests_against_emulator3.yml | 2 +- .../django_tests_against_emulator4.yml | 2 +- .../django_tests_against_emulator5.yml | 2 +- .../django_tests_against_emulator6.yml | 2 +- .../django_tests_against_emulator7.yml | 2 +- .../django_tests_against_emulator8.yml | 2 +- .../django_tests_against_emulator9.yml | 2 +- .../integration-tests-against-emulator.yml | 2 +- .kokoro/build.sh | 14 +- .kokoro/docker/docs/Dockerfile | 49 +-- .kokoro/docker/docs/fetch_gpg_keys.sh | 18 +- .kokoro/docs/common.cfg | 2 +- .kokoro/populate-secrets.sh | 35 +++ .kokoro/publish-docs.sh | 14 +- .kokoro/release.sh | 20 +- .kokoro/release/common.cfg | 42 +-- .kokoro/release/release.cfg | 2 +- .kokoro/samples/lint/common.cfg | 34 ++ .kokoro/samples/lint/continuous.cfg | 6 + .kokoro/samples/lint/periodic.cfg | 6 + .kokoro/samples/lint/presubmit.cfg | 6 + .kokoro/samples/python3.6/common.cfg | 40 +++ .kokoro/samples/python3.6/continuous.cfg | 7 + .kokoro/samples/python3.6/periodic-head.cfg | 11 + .kokoro/samples/python3.6/periodic.cfg | 6 + .kokoro/samples/python3.6/presubmit.cfg | 6 + .kokoro/samples/python3.7/common.cfg | 40 +++ .kokoro/samples/python3.7/continuous.cfg | 6 + .kokoro/samples/python3.7/periodic-head.cfg | 11 + .kokoro/samples/python3.7/periodic.cfg | 6 + .kokoro/samples/python3.7/presubmit.cfg | 6 + .kokoro/samples/python3.8/common.cfg | 40 +++ .kokoro/samples/python3.8/continuous.cfg | 6 + .kokoro/samples/python3.8/periodic-head.cfg | 11 + .kokoro/samples/python3.8/periodic.cfg | 6 + .kokoro/samples/python3.8/presubmit.cfg | 6 + .kokoro/samples/python3.9/common.cfg | 40 +++ .kokoro/samples/python3.9/continuous.cfg | 6 + .kokoro/samples/python3.9/periodic-head.cfg | 11 + .kokoro/samples/python3.9/periodic.cfg | 6 + .kokoro/samples/python3.9/presubmit.cfg | 6 + .kokoro/test-samples-against-head.sh | 21 ++ .kokoro/test-samples-impl.sh | 94 ++++++ .kokoro/test-samples.sh | 39 +++ .kokoro/trampoline.sh | 31 +- .kokoro/trampoline_v2.sh | 14 +- .trampolinerc | 2 +- CHANGELOG.md | 30 ++ README.rst | 31 +- assets/overview.png | Bin 48844 -> 62645 bytes django_spanner/__init__.py | 3 + django_spanner/features.py | 4 +- django_spanner/schema.py | 10 +- docs/changelog.md | 2 +- docs/example_from_scratch.md | 1 + docs/example_healthchecks.md | 1 + docs/samples.rst | 30 +- noxfile.py | 2 +- owlbot.py | 42 +++ setup.py | 2 +- tests/performance/__init__.py | 5 + tests/performance/django_spanner/__init__.py | 5 + tests/performance/django_spanner/models.py | 14 + .../django_spanner/test_benchmark.py | 296 ++++++++++++++++++ tests/system/django_spanner/models.py | 13 + .../django_spanner/test_check_constraint.py | 64 ++++ tests/system/django_spanner/test_decimal.py | 11 +- tests/system/django_spanner/utils.py | 3 +- version.py | 2 +- 74 files changed, 1106 insertions(+), 230 deletions(-) create mode 100644 .github/.OwlBot.lock.yaml create mode 100644 .github/.OwlBot.yaml create mode 100755 .kokoro/populate-secrets.sh create mode 100644 .kokoro/samples/lint/common.cfg create mode 100644 .kokoro/samples/lint/continuous.cfg create mode 100644 .kokoro/samples/lint/periodic.cfg create mode 100644 .kokoro/samples/lint/presubmit.cfg create mode 100644 .kokoro/samples/python3.6/common.cfg create mode 100644 .kokoro/samples/python3.6/continuous.cfg create mode 100644 .kokoro/samples/python3.6/periodic-head.cfg create mode 100644 .kokoro/samples/python3.6/periodic.cfg create mode 100644 .kokoro/samples/python3.6/presubmit.cfg create mode 100644 .kokoro/samples/python3.7/common.cfg create mode 100644 .kokoro/samples/python3.7/continuous.cfg create mode 100644 .kokoro/samples/python3.7/periodic-head.cfg create mode 100644 .kokoro/samples/python3.7/periodic.cfg create mode 100644 .kokoro/samples/python3.7/presubmit.cfg create mode 100644 .kokoro/samples/python3.8/common.cfg create mode 100644 .kokoro/samples/python3.8/continuous.cfg create mode 100644 .kokoro/samples/python3.8/periodic-head.cfg create mode 100644 .kokoro/samples/python3.8/periodic.cfg create mode 100644 .kokoro/samples/python3.8/presubmit.cfg create mode 100644 .kokoro/samples/python3.9/common.cfg create mode 100644 .kokoro/samples/python3.9/continuous.cfg create mode 100644 .kokoro/samples/python3.9/periodic-head.cfg create mode 100644 .kokoro/samples/python3.9/periodic.cfg create mode 100644 .kokoro/samples/python3.9/presubmit.cfg create mode 100755 .kokoro/test-samples-against-head.sh create mode 100755 .kokoro/test-samples-impl.sh create mode 100755 .kokoro/test-samples.sh mode change 100644 => 120000 docs/changelog.md create mode 120000 docs/example_from_scratch.md create mode 120000 docs/example_healthchecks.md create mode 100644 owlbot.py create mode 100644 tests/performance/__init__.py create mode 100644 tests/performance/django_spanner/__init__.py create mode 100644 tests/performance/django_spanner/models.py create mode 100644 tests/performance/django_spanner/test_benchmark.py create mode 100644 tests/system/django_spanner/test_check_constraint.py diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml new file mode 100644 index 0000000000..cd8bc8740c --- /dev/null +++ b/.github/.OwlBot.lock.yaml @@ -0,0 +1,9 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +docker: + image: gcr.io/repo-automation-bots/owlbot-python:latest + digest: sha256:0ffe3bdd6c7159692df5f7744da74e5ef19966288a6bf76023e8e04e0c424d7d diff --git a/.github/.OwlBot.yaml b/.github/.OwlBot.yaml new file mode 100644 index 0000000000..1bfc664928 --- /dev/null +++ b/.github/.OwlBot.yaml @@ -0,0 +1,8 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +docker: + image: gcr.io/repo-automation-bots/owlbot-python:latest diff --git a/.github/workflows/django_tests_against_emulator0.yml b/.github/workflows/django_tests_against_emulator0.yml index 0f7f7c99db..2b31049e72 100644 --- a/.github/workflows/django_tests_against_emulator0.yml +++ b/.github/workflows/django_tests_against_emulator0.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: django-tests0 jobs: diff --git a/.github/workflows/django_tests_against_emulator1.yml b/.github/workflows/django_tests_against_emulator1.yml index 921a9a5848..722a593af1 100644 --- a/.github/workflows/django_tests_against_emulator1.yml +++ b/.github/workflows/django_tests_against_emulator1.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: django-tests1 jobs: diff --git a/.github/workflows/django_tests_against_emulator2.yml b/.github/workflows/django_tests_against_emulator2.yml index 682de38ed6..364803ab3a 100644 --- a/.github/workflows/django_tests_against_emulator2.yml +++ b/.github/workflows/django_tests_against_emulator2.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: django-tests2 jobs: diff --git a/.github/workflows/django_tests_against_emulator3.yml b/.github/workflows/django_tests_against_emulator3.yml index ea83185a82..9e2df8ae94 100644 --- a/.github/workflows/django_tests_against_emulator3.yml +++ b/.github/workflows/django_tests_against_emulator3.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: django-tests3 jobs: diff --git a/.github/workflows/django_tests_against_emulator4.yml b/.github/workflows/django_tests_against_emulator4.yml index 6a59ff7725..361dccfbc7 100644 --- a/.github/workflows/django_tests_against_emulator4.yml +++ b/.github/workflows/django_tests_against_emulator4.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: django-tests4 jobs: diff --git a/.github/workflows/django_tests_against_emulator5.yml b/.github/workflows/django_tests_against_emulator5.yml index f07609fb2f..817b0c4295 100644 --- a/.github/workflows/django_tests_against_emulator5.yml +++ b/.github/workflows/django_tests_against_emulator5.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: django-tests5 jobs: diff --git a/.github/workflows/django_tests_against_emulator6.yml b/.github/workflows/django_tests_against_emulator6.yml index 3984852de2..ab9d817c98 100644 --- a/.github/workflows/django_tests_against_emulator6.yml +++ b/.github/workflows/django_tests_against_emulator6.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: django-tests6 jobs: diff --git a/.github/workflows/django_tests_against_emulator7.yml b/.github/workflows/django_tests_against_emulator7.yml index 0932197f86..4ee19f9373 100644 --- a/.github/workflows/django_tests_against_emulator7.yml +++ b/.github/workflows/django_tests_against_emulator7.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: django-tests7 jobs: diff --git a/.github/workflows/django_tests_against_emulator8.yml b/.github/workflows/django_tests_against_emulator8.yml index 98d4b5f1d8..5c148ddd23 100644 --- a/.github/workflows/django_tests_against_emulator8.yml +++ b/.github/workflows/django_tests_against_emulator8.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: django-tests8 jobs: diff --git a/.github/workflows/django_tests_against_emulator9.yml b/.github/workflows/django_tests_against_emulator9.yml index 0ba4baefce..4388c3cfa6 100644 --- a/.github/workflows/django_tests_against_emulator9.yml +++ b/.github/workflows/django_tests_against_emulator9.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: django-tests9 jobs: diff --git a/.github/workflows/integration-tests-against-emulator.yml b/.github/workflows/integration-tests-against-emulator.yml index efd2c4e5bb..5767e966bb 100644 --- a/.github/workflows/integration-tests-against-emulator.yml +++ b/.github/workflows/integration-tests-against-emulator.yml @@ -1,7 +1,7 @@ on: push: branches: - - master + - main pull_request: name: Run Django Spanner integration tests against emulator jobs: diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 4ac9dd8b76..eea1d84933 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -1,17 +1,9 @@ #!/bin/bash # Copyright 2020 Google LLC # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd set -eo pipefail diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile index 412b0b56a9..92ade8f99f 100644 --- a/.kokoro/docker/docs/Dockerfile +++ b/.kokoro/docker/docs/Dockerfile @@ -1,16 +1,8 @@ # Copyright 2020 Google LLC # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd from ubuntu:20.04 @@ -40,6 +32,7 @@ RUN apt-get update \ libssl-dev \ libsqlite3-dev \ portaudio19-dev \ + python3-distutils \ redis-server \ software-properties-common \ ssh \ @@ -59,40 +52,8 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* \ && rm -f /var/cache/apt/archives/*.deb - -COPY fetch_gpg_keys.sh /tmp -# Install the desired versions of Python. -RUN set -ex \ - && export GNUPGHOME="$(mktemp -d)" \ - && echo "disable-ipv6" >> "${GNUPGHOME}/dirmngr.conf" \ - && /tmp/fetch_gpg_keys.sh \ - && for PYTHON_VERSION in 3.7.8 3.8.5; do \ - wget --no-check-certificate -O python-${PYTHON_VERSION}.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \ - && wget --no-check-certificate -O python-${PYTHON_VERSION}.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \ - && gpg --batch --verify python-${PYTHON_VERSION}.tar.xz.asc python-${PYTHON_VERSION}.tar.xz \ - && rm -r python-${PYTHON_VERSION}.tar.xz.asc \ - && mkdir -p /usr/src/python-${PYTHON_VERSION} \ - && tar -xJC /usr/src/python-${PYTHON_VERSION} --strip-components=1 -f python-${PYTHON_VERSION}.tar.xz \ - && rm python-${PYTHON_VERSION}.tar.xz \ - && cd /usr/src/python-${PYTHON_VERSION} \ - && ./configure \ - --enable-shared \ - # This works only on Python 2.7 and throws a warning on every other - # version, but seems otherwise harmless. - --enable-unicode=ucs4 \ - --with-system-ffi \ - --without-ensurepip \ - && make -j$(nproc) \ - && make install \ - && ldconfig \ - ; done \ - && rm -rf "${GNUPGHOME}" \ - && rm -rf /usr/src/python* \ - && rm -rf ~/.cache/ - RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \ - && python3.7 /tmp/get-pip.py \ && python3.8 /tmp/get-pip.py \ && rm /tmp/get-pip.py -CMD ["python3.7"] +CMD ["python3.8"] diff --git a/.kokoro/docker/docs/fetch_gpg_keys.sh b/.kokoro/docker/docs/fetch_gpg_keys.sh index d653dd868e..c26695ddf0 100755 --- a/.kokoro/docker/docs/fetch_gpg_keys.sh +++ b/.kokoro/docker/docs/fetch_gpg_keys.sh @@ -1,21 +1,13 @@ #!/bin/bash # Copyright 2020 Google LLC # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd # A script to fetch gpg keys with retry. # Avoid jinja parsing the file. -# +# function retry { if [[ "${#}" -le 1 ]]; then @@ -42,4 +34,4 @@ retry 3 gpg --keyserver ha.pool.sks-keyservers.net --recv-keys \ retry 3 gpg --keyserver ha.pool.sks-keyservers.net --recv-keys \ E3FF2839C048B25C084DEBE9B26995E310250568 -# +# diff --git a/.kokoro/docs/common.cfg b/.kokoro/docs/common.cfg index 2003193838..cdf8e2a4ef 100644 --- a/.kokoro/docs/common.cfg +++ b/.kokoro/docs/common.cfg @@ -62,4 +62,4 @@ before_action { keyname: "docuploader_service_account" } } -} +} \ No newline at end of file diff --git a/.kokoro/populate-secrets.sh b/.kokoro/populate-secrets.sh new file mode 100755 index 0000000000..7b511c3257 --- /dev/null +++ b/.kokoro/populate-secrets.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +set -eo pipefail + +function now { date +"%Y-%m-%d %H:%M:%S" | tr -d '\n' ;} +function msg { println "$*" >&2 ;} +function println { printf '%s\n' "$(now) $*" ;} + + +# Populates requested secrets set in SECRET_MANAGER_KEYS from service account: +# kokoro-trampoline@cloud-devrel-kokoro-resources.iam.gserviceaccount.com +SECRET_LOCATION="${KOKORO_GFILE_DIR}/secret_manager" +msg "Creating folder on disk for secrets: ${SECRET_LOCATION}" +mkdir -p ${SECRET_LOCATION} +for key in $(echo ${SECRET_MANAGER_KEYS} | sed "s/,/ /g") +do + msg "Retrieving secret ${key}" + docker run --entrypoint=gcloud \ + --volume=${KOKORO_GFILE_DIR}:${KOKORO_GFILE_DIR} \ + gcr.io/google.com/cloudsdktool/cloud-sdk \ + secrets versions access latest \ + --project cloud-devrel-kokoro-resources \ + --secret ${key} > \ + "${SECRET_LOCATION}/${key}" + if [[ $? == 0 ]]; then + msg "Secret written to ${SECRET_LOCATION}/${key}" + else + msg "Error retrieving secret ${key}" + fi +done diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh index 8acb14e802..a80ea7c0a0 100755 --- a/.kokoro/publish-docs.sh +++ b/.kokoro/publish-docs.sh @@ -1,17 +1,9 @@ #!/bin/bash # Copyright 2020 Google LLC # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd set -eo pipefail diff --git a/.kokoro/release.sh b/.kokoro/release.sh index 9f60d5f251..57e2f28ce1 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -1,19 +1,9 @@ #!/bin/bash # Copyright 2020 Google LLC # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#!/bin/bash +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd set -eo pipefail @@ -28,7 +18,7 @@ python3 -m pip install --upgrade twine wheel setuptools export PYTHONUNBUFFERED=1 # Move into the package, build the distribution and upload. -TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google_cloud_pypi_password") +TWINE_PASSWORD=$(cat "${KOKORO_GFILE_DIR}/secret_manager/google-cloud-pypi-token") cd github/python-spanner-django python3 setup.py sdist bdist_wheel -twine upload --username gcloudpypi --password "${TWINE_PASSWORD}" dist/* +twine upload --username __token__ --password "${TWINE_PASSWORD}" dist/* diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg index 31533b977a..2cf743579d 100644 --- a/.kokoro/release/common.cfg +++ b/.kokoro/release/common.cfg @@ -23,42 +23,8 @@ env_vars: { value: "github/python-spanner-django/.kokoro/release.sh" } -# Fetch the token needed for reporting release status to GitHub -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "yoshi-automation-github-key" - } - } -} - -# Fetch PyPI password -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "google_cloud_pypi_password" - } - } -} - -# Fetch magictoken to use with Magic Github Proxy -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "releasetool-magictoken" - } - } -} - -# Fetch api key to use with Magic Github Proxy -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "magic-github-proxy-api-key" - } - } +# Tokens needed to report release status back to GitHub +env_vars: { + key: "SECRET_MANAGER_KEYS" + value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem,google-cloud-pypi-token" } diff --git a/.kokoro/release/release.cfg b/.kokoro/release/release.cfg index 18a4c35325..8f43917d92 100644 --- a/.kokoro/release/release.cfg +++ b/.kokoro/release/release.cfg @@ -1 +1 @@ -# Format: //devtools/kokoro/config/proto/build.proto +# Format: //devtools/kokoro/config/proto/build.proto \ No newline at end of file diff --git a/.kokoro/samples/lint/common.cfg b/.kokoro/samples/lint/common.cfg new file mode 100644 index 0000000000..b42e774271 --- /dev/null +++ b/.kokoro/samples/lint/common.cfg @@ -0,0 +1,34 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "lint" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-spanner-django/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-spanner-django/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/lint/continuous.cfg b/.kokoro/samples/lint/continuous.cfg new file mode 100644 index 0000000000..a1c8d9759c --- /dev/null +++ b/.kokoro/samples/lint/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/lint/periodic.cfg b/.kokoro/samples/lint/periodic.cfg new file mode 100644 index 0000000000..50fec96497 --- /dev/null +++ b/.kokoro/samples/lint/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/lint/presubmit.cfg b/.kokoro/samples/lint/presubmit.cfg new file mode 100644 index 0000000000..a1c8d9759c --- /dev/null +++ b/.kokoro/samples/lint/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.6/common.cfg b/.kokoro/samples/python3.6/common.cfg new file mode 100644 index 0000000000..6e32d40d20 --- /dev/null +++ b/.kokoro/samples/python3.6/common.cfg @@ -0,0 +1,40 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.6" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py36" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-spanner-django/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-spanner-django/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.6/continuous.cfg b/.kokoro/samples/python3.6/continuous.cfg new file mode 100644 index 0000000000..7218af1499 --- /dev/null +++ b/.kokoro/samples/python3.6/continuous.cfg @@ -0,0 +1,7 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + diff --git a/.kokoro/samples/python3.6/periodic-head.cfg b/.kokoro/samples/python3.6/periodic-head.cfg new file mode 100644 index 0000000000..f9cfcd33e0 --- /dev/null +++ b/.kokoro/samples/python3.6/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.6/periodic.cfg b/.kokoro/samples/python3.6/periodic.cfg new file mode 100644 index 0000000000..50fec96497 --- /dev/null +++ b/.kokoro/samples/python3.6/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.6/presubmit.cfg b/.kokoro/samples/python3.6/presubmit.cfg new file mode 100644 index 0000000000..a1c8d9759c --- /dev/null +++ b/.kokoro/samples/python3.6/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.7/common.cfg new file mode 100644 index 0000000000..407c854ff0 --- /dev/null +++ b/.kokoro/samples/python3.7/common.cfg @@ -0,0 +1,40 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.7" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py37" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-spanner-django/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-spanner-django/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.7/continuous.cfg b/.kokoro/samples/python3.7/continuous.cfg new file mode 100644 index 0000000000..a1c8d9759c --- /dev/null +++ b/.kokoro/samples/python3.7/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/periodic-head.cfg b/.kokoro/samples/python3.7/periodic-head.cfg new file mode 100644 index 0000000000..f9cfcd33e0 --- /dev/null +++ b/.kokoro/samples/python3.7/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.7/periodic.cfg b/.kokoro/samples/python3.7/periodic.cfg new file mode 100644 index 0000000000..50fec96497 --- /dev/null +++ b/.kokoro/samples/python3.7/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/presubmit.cfg b/.kokoro/samples/python3.7/presubmit.cfg new file mode 100644 index 0000000000..a1c8d9759c --- /dev/null +++ b/.kokoro/samples/python3.7/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/common.cfg b/.kokoro/samples/python3.8/common.cfg new file mode 100644 index 0000000000..07b0e8fddb --- /dev/null +++ b/.kokoro/samples/python3.8/common.cfg @@ -0,0 +1,40 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.8" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py38" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-spanner-django/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-spanner-django/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.8/continuous.cfg b/.kokoro/samples/python3.8/continuous.cfg new file mode 100644 index 0000000000..a1c8d9759c --- /dev/null +++ b/.kokoro/samples/python3.8/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/periodic-head.cfg b/.kokoro/samples/python3.8/periodic-head.cfg new file mode 100644 index 0000000000..f9cfcd33e0 --- /dev/null +++ b/.kokoro/samples/python3.8/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.8/periodic.cfg b/.kokoro/samples/python3.8/periodic.cfg new file mode 100644 index 0000000000..50fec96497 --- /dev/null +++ b/.kokoro/samples/python3.8/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/presubmit.cfg b/.kokoro/samples/python3.8/presubmit.cfg new file mode 100644 index 0000000000..a1c8d9759c --- /dev/null +++ b/.kokoro/samples/python3.8/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.9/common.cfg b/.kokoro/samples/python3.9/common.cfg new file mode 100644 index 0000000000..0c8833cfa5 --- /dev/null +++ b/.kokoro/samples/python3.9/common.cfg @@ -0,0 +1,40 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.9" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-py39" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-spanner-django/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-spanner-django/.kokoro/trampoline.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.9/continuous.cfg b/.kokoro/samples/python3.9/continuous.cfg new file mode 100644 index 0000000000..a1c8d9759c --- /dev/null +++ b/.kokoro/samples/python3.9/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.9/periodic-head.cfg b/.kokoro/samples/python3.9/periodic-head.cfg new file mode 100644 index 0000000000..f9cfcd33e0 --- /dev/null +++ b/.kokoro/samples/python3.9/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.9/periodic.cfg b/.kokoro/samples/python3.9/periodic.cfg new file mode 100644 index 0000000000..50fec96497 --- /dev/null +++ b/.kokoro/samples/python3.9/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.9/presubmit.cfg b/.kokoro/samples/python3.9/presubmit.cfg new file mode 100644 index 0000000000..a1c8d9759c --- /dev/null +++ b/.kokoro/samples/python3.9/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/test-samples-against-head.sh b/.kokoro/test-samples-against-head.sh new file mode 100755 index 0000000000..104d0e40bb --- /dev/null +++ b/.kokoro/test-samples-against-head.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + + +# A customized test runner for samples. +# +# For periodic builds, you can specify this file for testing against head. + +# `-e` enables the script to automatically fail when a command fails +# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero +set -eo pipefail +# Enables `**` to include files nested inside sub-folders +shopt -s globstar + +cd github/python-spanner-django + +exec .kokoro/test-samples-impl.sh diff --git a/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh new file mode 100755 index 0000000000..595dca8b0d --- /dev/null +++ b/.kokoro/test-samples-impl.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + + +# `-e` enables the script to automatically fail when a command fails +# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero +set -eo pipefail +# Enables `**` to include files nested inside sub-folders +shopt -s globstar + +# Exit early if samples don't exist +if ! find samples -name 'requirements.txt' | grep -q .; then + echo "No tests run. './samples/**/requirements.txt' not found" + exit 0 +fi + +# Disable buffering, so that the logs stream through. +export PYTHONUNBUFFERED=1 + +# Debug: show build environment +env | grep KOKORO + +# Install nox +python3.6 -m pip install --upgrade --quiet nox + +# Use secrets acessor service account to get secrets +if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then + gcloud auth activate-service-account \ + --key-file="${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" \ + --project="cloud-devrel-kokoro-resources" +fi + +# This script will create 3 files: +# - testing/test-env.sh +# - testing/service-account.json +# - testing/client-secrets.json +./scripts/decrypt-secrets.sh + +source ./testing/test-env.sh +export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/testing/service-account.json + +# For cloud-run session, we activate the service account for gcloud sdk. +gcloud auth activate-service-account \ + --key-file "${GOOGLE_APPLICATION_CREDENTIALS}" + +export GOOGLE_CLIENT_SECRETS=$(pwd)/testing/client-secrets.json + +echo -e "\n******************** TESTING PROJECTS ********************" + +# Switch to 'fail at end' to allow all tests to complete before exiting. +set +e +# Use RTN to return a non-zero value if the test fails. +RTN=0 +ROOT=$(pwd) +# Find all requirements.txt in the samples directory (may break on whitespace). +for file in samples/**/requirements.txt; do + cd "$ROOT" + # Navigate to the project folder. + file=$(dirname "$file") + cd "$file" + + echo "------------------------------------------------------------" + echo "- testing $file" + echo "------------------------------------------------------------" + + # Use nox to execute the tests for the project. + python3.6 -m nox -s "$RUN_TESTS_SESSION" + EXIT=$? + + # If this is a periodic build, send the test log to the FlakyBot. + # See https://github.com/googleapis/repo-automation-bots/tree/main/packages/flakybot. + if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then + chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot + $KOKORO_GFILE_DIR/linux_amd64/flakybot + fi + + if [[ $EXIT -ne 0 ]]; then + RTN=1 + echo -e "\n Testing failed: Nox returned a non-zero exit code. \n" + else + echo -e "\n Testing completed.\n" + fi + +done +cd "$ROOT" + +# Workaround for Kokoro permissions issue: delete secrets +rm testing/{test-env.sh,client-secrets.json,service-account.json} + +exit "$RTN" diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh new file mode 100755 index 0000000000..64bd21dd92 --- /dev/null +++ b/.kokoro/test-samples.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + + +# The default test runner for samples. +# +# For periodic builds, we rewinds the repo to the latest release, and +# run test-samples-impl.sh. + +# `-e` enables the script to automatically fail when a command fails +# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero +set -eo pipefail +# Enables `**` to include files nested inside sub-folders +shopt -s globstar + +cd github/python-spanner-django + +# Run periodic samples tests at latest release +if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then + # preserving the test runner implementation. + cp .kokoro/test-samples-impl.sh "${TMPDIR}/test-samples-impl.sh" + echo "--- IMPORTANT IMPORTANT IMPORTANT ---" + echo "Now we rewind the repo back to the latest release..." + LATEST_RELEASE=$(git describe --abbrev=0 --tags) + git checkout $LATEST_RELEASE + echo "The current head is: " + echo $(git rev-parse --verify HEAD) + echo "--- IMPORTANT IMPORTANT IMPORTANT ---" + # move back the test runner implementation if there's no file. + if [ ! -f .kokoro/test-samples-impl.sh ]; then + cp "${TMPDIR}/test-samples-impl.sh" .kokoro/test-samples-impl.sh + fi +fi + +exec .kokoro/test-samples-impl.sh diff --git a/.kokoro/trampoline.sh b/.kokoro/trampoline.sh index 8f0237f322..d42a40acbb 100755 --- a/.kokoro/trampoline.sh +++ b/.kokoro/trampoline.sh @@ -1,23 +1,20 @@ #!/bin/bash -# Copyright 2020 Google LLC. +# Copyright 2017 Google LLC # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd set -eo pipefail -python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" || ret_code=$? - -chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh -${KOKORO_GFILE_DIR}/trampoline_cleanup.sh || true +# Always run the cleanup script, regardless of the success of bouncing into +# the container. +function cleanup() { + chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh + ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh + echo "cleanup"; +} +trap cleanup EXIT -exit ${ret_code} +$(dirname $0)/populate-secrets.sh # Secret Manager secrets. +python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" \ No newline at end of file diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 4af6cdc26d..591faf816e 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -1,17 +1,9 @@ #!/usr/bin/env bash # Copyright 2020 Google LLC # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd # trampoline_v2.sh # diff --git a/.trampolinerc b/.trampolinerc index 28037bbb86..383b6ec89f 100644 --- a/.trampolinerc +++ b/.trampolinerc @@ -1,4 +1,4 @@ -# Copyright 2021 Google LLC +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d1d5d4875..a370dd14c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## 2.2.1b3 (2021-07-30) + +### Miscellaneous Chores +- release 2.2.1b3 ([de23f65](https://www.github.com/googleapis/python-spanner-django/commit/de23f65)) +- Update repo to say beta release instead of alpha ([#691](https://www.github.com/googleapis/python-spanner-django/issues/691)) ([2144d09](https://www.github.com/googleapis/python-spanner-django/commit/2144d09)) + + +## 2.2.1b2 (2021-07-27) + + +### Features +- Added support for check constraint ([#679](https://www.github.com/googleapis/python-spanner-django/issues/679)) ([42352c0](https://www.github.com/googleapis/python-spanner-django/commit/42352c0)) +- Add open telemetry trace in schema and related unit tests ([#648](https://www.github.com/googleapis/python-spanner-django/issues/648)) ([fc51086](https://www.github.com/googleapis/python-spanner-django/commit/fc51086)) + + +### Bug Fixes +- updated assets to have text background so it works with dark mode ([#674](https://www.github.com/googleapis/python-spanner-django/issues/674)) ([306eeba](https://www.github.com/googleapis/python-spanner-django/commit/306eeba)) +- updated assets to have text background so it works with dark mode ([#671](https://www.github.com/googleapis/python-spanner-django/issues/671)) ([0f99938](https://www.github.com/googleapis/python-spanner-django/commit/0f99938)) +- bump version number after 2.2.1b1 release ([#652](https://www.github.com/googleapis/python-spanner-django/issues/652)) ([287b893](https://www.github.com/googleapis/python-spanner-django/commit/287b893)) + + +### Documentation +- update docs to show decimal field support and check constraints but no support for unsigned data type ([#683](https://www.github.com/googleapis/python-spanner-django/issues/683)) ([74f2269](https://www.github.com/googleapis/python-spanner-django/commit/74f2269)) +- Adding documentation for GA ([#665](https://www.github.com/googleapis/python-spanner-django/issues/665)) ([216c2e0](https://www.github.com/googleapis/python-spanner-django/commit/216c2e0)) + + +### Miscellaneous Chores +- release 2.2.1b2 ([#685](https://www.github.com/googleapis/python-spanner-django/issues/685)) ([96a809d](https://www.github.com/googleapis/python-spanner-django/commit/96a809d)) +- fix release build ([#659](https://www.github.com/googleapis/python-spanner-django/issues/659)) ([11bc9c2](https://www.github.com/googleapis/python-spanner-django/commit/11bc9c2)) + ## 2.2.1b1 (2021-06-17) diff --git a/README.rst b/README.rst index e79e23cd91..62739a3182 100644 --- a/README.rst +++ b/README.rst @@ -220,13 +220,13 @@ How it works Overall design ~~~~~~~~~~~~~~ -.. figure:: https://raw.githubusercontent.com/googleapis/python-spanner-django/master/assets/overview.png +.. figure:: https://raw.githubusercontent.com/googleapis/python-spanner-django/main/assets/overview.png :alt: "Overall Design" Internals ~~~~~~~~~ -.. figure:: https://raw.githubusercontent.com/googleapis/python-spanner-django/master/assets/internals.png +.. figure:: https://raw.githubusercontent.com/googleapis/python-spanner-django/main/assets/internals.png :alt: "Internals" @@ -247,11 +247,11 @@ HOW TO CONTRIBUTE Contributions to this library are always welcome and highly encouraged. -See `CONTRIBUTING `_ for more information on how to get started. +See `CONTRIBUTING `_ for more information on how to get started. Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms. See the `Code -of Conduct `_ for more information. +of Conduct `_ for more information. Current limitations ------------------- @@ -283,23 +283,16 @@ were created. Spanner does not support ``ON DELETE CASCADE`` when creating foreign-key constraints, so this is not supported in ``django-google-spanner``. -Check constraints aren't supported -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``Unsigned`` datatypes are not supported +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Spanner does not support ``CHECK`` constraints so one isn't created for -`PositiveIntegerField +Spanner does not support ``Unsigned`` datatypes so `PositiveIntegerField `__ -and `CheckConstraint -`__ -can't be used. - -No native support for ``DecimalField`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Spanner's support for `Decimal `__ -types is limited to -`NUMERIC `__ -precision. Higher-precision values can be stored as strings instead. +and `PositiveSmallIntegerField +`__ +are both stored as `Integer type +`__ +. ``Meta.order_with_respect_to`` model option isn't supported ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/assets/overview.png b/assets/overview.png index a556727a18d3f0324be1207f21b67fa3a4a65718..1fc2933e518b77bc32b8cd605ec82787901ba660 100644 GIT binary patch literal 62645 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�T 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 literal 48844 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 h?~f5Ei@oaHoga>D5oY{DQyw<{{Sc7bv*z8 diff --git a/django_spanner/__init__.py b/django_spanner/__init__.py index 861e3abb94..a26703d5a5 100644 --- a/django_spanner/__init__.py +++ b/django_spanner/__init__.py @@ -5,6 +5,7 @@ # https://developers.google.com/open-source/licenses/bsd import datetime +import os # Monkey-patch AutoField to generate a random value since Cloud Spanner can't # do that. @@ -24,6 +25,8 @@ __version__ = pkg_resources.get_distribution("django-google-spanner").version +USE_EMULATOR = os.getenv("SPANNER_EMULATOR_HOST") is not None + check_django_compatability() register_expressions() register_functions() diff --git a/django_spanner/features.py b/django_spanner/features.py index af7e4c1131..a0ae6299c3 100644 --- a/django_spanner/features.py +++ b/django_spanner/features.py @@ -38,6 +38,8 @@ class DatabaseFeatures(BaseDatabaseFeatures): skip_tests = ( # No foreign key constraints in Spanner. "backends.tests.FkConstraintsTests.test_check_constraints", + # Spanner does not support empty list of DML statement. + "backends.tests.BackendTestCase.test_cursor_executemany_with_empty_params_list", "fixtures_regress.tests.TestFixtures.test_loaddata_raises_error_when_fixture_has_invalid_foreign_key", # No Django transaction management in Spanner. "basic.tests.SelectOnSaveTests.test_select_on_save_lying_update", @@ -184,7 +186,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): "db_functions.comparison.test_cast.CastTests.test_cast_to_decimal_field", "model_fields.test_decimalfield.DecimalFieldTests.test_fetch_from_db_without_float_rounding", "model_fields.test_decimalfield.DecimalFieldTests.test_roundtrip_with_trailing_zeros", - # No CHECK constraints in Spanner. + # Spanner does not support unsigned integer field. "model_fields.test_integerfield.PositiveIntegerFieldTests.test_negative_values", # Spanner doesn't support the variance the standard deviation database # functions: diff --git a/django_spanner/schema.py b/django_spanner/schema.py index d28dcc4f6e..247358857a 100644 --- a/django_spanner/schema.py +++ b/django_spanner/schema.py @@ -7,6 +7,7 @@ from django.db import NotSupportedError from django.db.backends.base.schema import BaseDatabaseSchemaEditor from django_spanner._opentelemetry_tracing import trace_call +from django_spanner import USE_EMULATOR class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): @@ -472,8 +473,13 @@ def _alter_column_type_sql(self, model, old_field, new_field, new_type): ) def _check_sql(self, name, check): - # Spanner doesn't support CHECK constraints. - return None + # Emulator does not support check constraints yet. + if USE_EMULATOR: + return None + return self.sql_constraint % { + "name": self.quote_name(name), + "constraint": self.sql_check_constraint % {"check": check}, + } def _unique_sql(self, model, fields, name, condition=None): # Inline constraints aren't supported, so create the index separately. diff --git a/docs/changelog.md b/docs/changelog.md deleted file mode 100644 index 825c32f0d0..0000000000 --- a/docs/changelog.md +++ /dev/null @@ -1 +0,0 @@ -# Changelog diff --git a/docs/changelog.md b/docs/changelog.md new file mode 120000 index 0000000000..04c99a55ca --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1 @@ +../CHANGELOG.md \ No newline at end of file diff --git a/docs/example_from_scratch.md b/docs/example_from_scratch.md new file mode 120000 index 0000000000..1e40292b5e --- /dev/null +++ b/docs/example_from_scratch.md @@ -0,0 +1 @@ +../examples/from-scratch/README.md \ No newline at end of file diff --git a/docs/example_healthchecks.md b/docs/example_healthchecks.md new file mode 120000 index 0000000000..08983d890b --- /dev/null +++ b/docs/example_healthchecks.md @@ -0,0 +1 @@ +../examples/healthchecks/README.md \ No newline at end of file diff --git a/docs/samples.rst b/docs/samples.rst index 4d9ef417a2..09d8c37590 100644 --- a/docs/samples.rst +++ b/docs/samples.rst @@ -1,24 +1,12 @@ -Sample Code -#################################### +Sample Examples +############### -Create and register your first model -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To define your database layout create a models file in your app folder and add the relevant -classes to it. Spanner works exactly like any other database you may have used with Django. -Here is a simple example you can run with Spanner. In our poll application below we create -the following two models: +django-spanner for Django tutorial +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. code:: python +This `Example `_ shows how to use django-spanner for Cloud Spanner as a backend database for `healthchecks.io `_ - from django.db import models - - class Question(models.Model): - question_text = models.CharField(max_length=200) - pub_date = models.DateTimeField('date published') - def __str__(self): - return str(self.rating) - - class Choice(models.Model): - question = models.ForeignKey(Question, on_delete=models.CASCADE) - choice_text = models.CharField(max_length=200) - votes = models.IntegerField(default=0) +django-spanner on healthchecks.io +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This `Example `_ shows how to use django-spanner for Cloud Spanner as a backend database for `Django's tutorials `_ diff --git a/noxfile.py b/noxfile.py index a5c05e7a02..3b51d73841 100644 --- a/noxfile.py +++ b/noxfile.py @@ -43,7 +43,7 @@ def lint(session): session.run("flake8", "django_spanner", "tests") -@nox.session(python="3.6") +@nox.session(python=DEFAULT_PYTHON_VERSION) def blacken(session): """Run black. diff --git a/owlbot.py b/owlbot.py new file mode 100644 index 0000000000..d0087b4c0b --- /dev/null +++ b/owlbot.py @@ -0,0 +1,42 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +"""This script is used to synthesize generated parts of this library.""" +import re + +import synthtool as s +import synthtool.gcp as gcp + +# ---------------------------------------------------------------------------- +# Add templated files +# ---------------------------------------------------------------------------- +templated_files = gcp.CommonTemplates().py_library(microgenerator=True) + +# Just move templates for building docs and releases +# Presubmit and continuous are configured differently +s.move(templated_files / ".trampolinerc") +s.move(templated_files / ".kokoro" / "docker") +s.move(templated_files / ".kokoro" / "docs") +s.move(templated_files / ".kokoro" / "release.sh") +s.move(templated_files / ".kokoro" / "trampoline_v2.sh") +s.move(templated_files / ".kokoro" / "trampoline.sh") +s.move(templated_files / ".kokoro" / "populate-secrets.sh") +s.move(templated_files / ".kokoro" / "release") + +# Replace the Apache Licenses in the `.kokoro` directory +# with the BSD license expected in this repository +s.replace( + ".kokoro/**/*", + "# Copyright.*(\d{4}).*# limitations under the License\.", + """# Copyright \g<1> Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd""", + flags=re.DOTALL +) + +s.shell.run(["nox", "-s", "blacken"], hide_output=False) diff --git a/setup.py b/setup.py index c310fda167..a3143566bb 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ # 'Development Status :: 3 - Alpha' # 'Development Status :: 4 - Beta' # 'Development Status :: 5 - Production/Stable' -release_status = "Development Status :: 3 - Alpha" +release_status = "Development Status :: 4 - Beta" dependencies = ["sqlparse >= 0.3.0", "google-cloud-spanner >= 3.0.0"] extras = { "tracing": [ diff --git a/tests/performance/__init__.py b/tests/performance/__init__.py new file mode 100644 index 0000000000..529352b757 --- /dev/null +++ b/tests/performance/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd diff --git a/tests/performance/django_spanner/__init__.py b/tests/performance/django_spanner/__init__.py new file mode 100644 index 0000000000..529352b757 --- /dev/null +++ b/tests/performance/django_spanner/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd diff --git a/tests/performance/django_spanner/models.py b/tests/performance/django_spanner/models.py new file mode 100644 index 0000000000..d387a6ef86 --- /dev/null +++ b/tests/performance/django_spanner/models.py @@ -0,0 +1,14 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +from django.db import models + + +class Author(models.Model): + id = models.IntegerField(primary_key=True) + first_name = models.CharField(max_length=20) + last_name = models.CharField(max_length=20) + rating = models.CharField(max_length=50) diff --git a/tests/performance/django_spanner/test_benchmark.py b/tests/performance/django_spanner/test_benchmark.py new file mode 100644 index 0000000000..31dd8f2987 --- /dev/null +++ b/tests/performance/django_spanner/test_benchmark.py @@ -0,0 +1,296 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +import random +import time +import unittest + +import pandas as pd +import pytest +from django.db import connection +from google.api_core.exceptions import Aborted +from google.cloud import spanner_dbapi +from google.cloud.spanner_v1 import Client, KeySet + +from tests.performance.django_spanner.models import Author +from tests.settings import DATABASE_NAME, INSTANCE_ID +from tests.system.django_spanner.utils import setup_database, setup_instance + + +def measure_execution_time(function): + """Decorator to measure a wrapped method execution time.""" + + def wrapper(self, measures): + """Execute the wrapped method and measure its execution time. + Args: + measures (dict): Test cases and their execution time. + """ + t_start = time.time() + try: + function(self) + measures[function.__name__] = round(time.time() - t_start, 4) + except Aborted: + measures[function.__name__] = 0 + + return wrapper + + +def insert_one_row(transaction, one_row): + """A transaction-function for the original Spanner client. + Inserts a single row into a database and then fetches it back. + """ + transaction.execute_update( + "INSERT Author (id, first_name, last_name, rating) " + " VALUES {}".format(str(one_row)) + ) + last_name = transaction.execute_sql( + "SELECT last_name FROM Author WHERE id=1" + ).one()[0] + if last_name != "Allison": + raise ValueError("Received invalid last name: " + last_name) + + +def insert_many_rows(transaction, many_rows): + """A transaction-function for the original Spanner client. + Insert 100 rows into a database. + """ + statements = [] + for row in many_rows: + statements.append( + "INSERT Author (id, first_name, last_name, rating) " + " VALUES {}".format(str(row)) + ) + _, count = transaction.batch_update(statements) + if sum(count) != 99: + raise ValueError("Wrong number of inserts: " + str(sum(count))) + + +class DjangoBenchmarkTest: + """The Django performace testing class.""" + + def __init__(self): + with connection.schema_editor() as editor: + # Create the tables + editor.create_model(Author) + + self._many_rows = [] + self._many_rows2 = [] + for i in range(99): + num = round(random.randint(0, 100000000)) + self._many_rows.append(Author(num, "Pete", "Allison", "2.1")) + num2 = round(random.randint(0, 100000000)) + self._many_rows2.append(Author(num2, "Pete", "Allison", "2.1")) + + @measure_execution_time + def insert_one_row_with_fetch_after(self): + author_kent = Author( + id=2, first_name="Pete", last_name="Allison", rating="2.1", + ) + author_kent.save() + last_name = Author.objects.get(pk=author_kent.id).last_name + if last_name != "Allison": + raise ValueError("Received invalid last name: " + last_name) + + @measure_execution_time + def insert_many_rows(self): + for row in self._many_rows: + row.save() + + @measure_execution_time + def insert_many_rows_with_mutations(self): + Author.objects.bulk_create(self._many_rows2) + + @measure_execution_time + def read_one_row(self): + row = Author.objects.all().first() + if row is None: + raise ValueError("No rows read") + + @measure_execution_time + def select_many_rows(self): + rows = Author.objects.all() + if len(rows) != 100: + raise ValueError("Wrong number of rows read") + + def _cleanup(self): + """Drop the test table.""" + with connection.schema_editor() as editor: + # delete the table + editor.delete_model(Author) + + def run(self): + """Execute every test case.""" + measures = {} + for method in ( + self.insert_one_row_with_fetch_after, + self.read_one_row, + self.insert_many_rows, + self.select_many_rows, + self.insert_many_rows_with_mutations, + ): + method(measures) + self._cleanup() + return measures + + +class SpannerBenchmarkTest: + """The Spanner performace testing class.""" + + def __init__(self): + self._create_table() + self._one_row = ( + 1, + "Pete", + "Allison", + "2.1", + ) + self._client = Client() + self._instance = self._client.instance(INSTANCE_ID) + self._database = self._instance.database(DATABASE_NAME) + + self._many_rows = [] + self._many_rows2 = [] + for i in range(99): + num = round(random.randint(0, 100000000)) + self._many_rows.append((num, "Pete", "Allison", "2.1")) + num2 = round(random.randint(0, 100000000)) + self._many_rows2.append((num2, "Pete", "Allison", "2.1")) + + # initiate a session + with self._database.snapshot(): + pass + + def _create_table(self): + """Create a table for performace testing.""" + conn = spanner_dbapi.connect(INSTANCE_ID, DATABASE_NAME) + conn.database.update_ddl( + [ + """ +CREATE TABLE Author ( + id INT64, + first_name STRING(20), + last_name STRING(20), + rating STRING(50), +) PRIMARY KEY (id) + """ + ] + ).result(120) + + conn.close() + + @measure_execution_time + def insert_one_row_with_fetch_after(self): + self._database.run_in_transaction(insert_one_row, self._one_row) + + @measure_execution_time + def insert_many_rows(self): + self._database.run_in_transaction(insert_many_rows, self._many_rows) + + @measure_execution_time + def insert_many_rows_with_mutations(self): + with self._database.batch() as batch: + batch.insert( + table="Author", + columns=("id", "first_name", "last_name", "rating"), + values=self._many_rows2, + ) + + @measure_execution_time + def read_one_row(self): + with self._database.snapshot() as snapshot: + keyset = KeySet(all_=True) + snapshot.read( + table="Author", + columns=("id", "first_name", "last_name", "rating"), + keyset=keyset, + ).one() + + @measure_execution_time + def select_many_rows(self): + with self._database.snapshot() as snapshot: + rows = list( + snapshot.execute_sql("SELECT * FROM Author ORDER BY last_name") + ) + if len(rows) != 100: + raise ValueError("Wrong number of rows read") + + def _cleanup(self): + """Drop the test table.""" + conn = spanner_dbapi.connect(INSTANCE_ID, DATABASE_NAME) + conn.database.update_ddl(["DROP TABLE Author"]) + conn.close() + + def run(self): + """Execute every test case.""" + measures = {} + for method in ( + self.insert_one_row_with_fetch_after, + self.read_one_row, + self.insert_many_rows, + self.select_many_rows, + self.insert_many_rows_with_mutations, + ): + method(measures) + self._cleanup() + return measures + + +@pytest.mark.django_db() +class BenchmarkTest(unittest.TestCase): + def setUp(self): + setup_instance() + setup_database() + + def test_run(self): + django_obj = pd.DataFrame( + columns=[ + "insert_one_row_with_fetch_after", + "read_one_row", + "insert_many_rows", + "select_many_rows", + "insert_many_rows_with_mutations", + ] + ) + spanner_obj = pd.DataFrame( + columns=[ + "insert_one_row_with_fetch_after", + "read_one_row", + "insert_many_rows", + "select_many_rows", + "insert_many_rows_with_mutations", + ] + ) + + for _ in range(50): + django_obj = django_obj.append( + DjangoBenchmarkTest().run(), ignore_index=True + ) + spanner_obj = spanner_obj.append( + SpannerBenchmarkTest().run(), ignore_index=True + ) + + avg = pd.concat( + [django_obj.mean(axis=0), spanner_obj.mean(axis=0)], axis=1 + ) + avg.columns = ["Django", "Spanner"] + std = pd.concat( + [django_obj.std(axis=0), spanner_obj.std(axis=0)], axis=1 + ) + std.columns = ["Django", "Spanner"] + err = pd.concat( + [django_obj.sem(axis=0), spanner_obj.sem(axis=0)], axis=1 + ) + err.columns = ["Django", "Spanner"] + + print( + "Average: ", + avg, + "Standard Deviation: ", + std, + "Error:", + err, + sep="\n", + ) diff --git a/tests/system/django_spanner/models.py b/tests/system/django_spanner/models.py index 5524ad8ec9..f7153ba994 100644 --- a/tests/system/django_spanner/models.py +++ b/tests/system/django_spanner/models.py @@ -21,3 +21,16 @@ class Number(models.Model): def __str__(self): return str(self.num) + + +class Event(models.Model): + start_date = models.DateTimeField() + end_date = models.DateTimeField() + + class Meta: + constraints = [ + models.CheckConstraint( + check=models.Q(end_date__gt=models.F("start_date")), + name="check_start_date", + ), + ] diff --git a/tests/system/django_spanner/test_check_constraint.py b/tests/system/django_spanner/test_check_constraint.py new file mode 100644 index 0000000000..9177166ce9 --- /dev/null +++ b/tests/system/django_spanner/test_check_constraint.py @@ -0,0 +1,64 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +from .models import Event +from django.test import TransactionTestCase +import datetime +import unittest +from django.utils import timezone +from google.api_core.exceptions import OutOfRange +from django.db import connection +from django_spanner import USE_EMULATOR +from tests.system.django_spanner.utils import ( + setup_instance, + teardown_instance, + setup_database, + teardown_database, +) + + +@unittest.skipIf( + USE_EMULATOR, "Check Constraint is not implemented in emulator." +) +class TestCheckConstraint(TransactionTestCase): + @classmethod + def setUpClass(cls): + setup_instance() + setup_database() + with connection.schema_editor() as editor: + # Create the table + editor.create_model(Event) + + @classmethod + def tearDownClass(cls): + with connection.schema_editor() as editor: + # delete the table + editor.delete_model(Event) + teardown_database() + teardown_instance() + + def test_insert_valid_value(self): + """ + Tests model object creation with Event model. + """ + now = timezone.now() + now_plus_10 = now + datetime.timedelta(minutes=10) + event_valid = Event(start_date=now, end_date=now_plus_10) + event_valid.save() + qs1 = Event.objects.filter().values("start_date") + self.assertEqual(qs1[0]["start_date"], now) + # Delete data from Event table. + Event.objects.all().delete() + + def test_insert_invalid_value(self): + """ + Tests model object creation with invalid data in Event model. + """ + now = timezone.now() + now_minus_1_day = now - timezone.timedelta(days=1) + event_invalid = Event(start_date=now, end_date=now_minus_1_day) + with self.assertRaises(OutOfRange): + event_invalid.save() diff --git a/tests/system/django_spanner/test_decimal.py b/tests/system/django_spanner/test_decimal.py index 73df7e796b..4155599af1 100644 --- a/tests/system/django_spanner/test_decimal.py +++ b/tests/system/django_spanner/test_decimal.py @@ -6,14 +6,13 @@ from .models import Author, Number from django.test import TransactionTestCase -from django.db import connection, ProgrammingError +from django.db import connection from decimal import Decimal from tests.system.django_spanner.utils import ( setup_instance, teardown_instance, setup_database, teardown_database, - USE_EMULATOR, ) @@ -87,12 +86,8 @@ def test_decimal_precision_limit(self): Tests decimal object precission limit. """ num_val = Number(num=Decimal(1) / Decimal(3)) - if USE_EMULATOR: - with self.assertRaises(ValueError): - num_val.save() - else: - with self.assertRaises(ProgrammingError): - num_val.save() + with self.assertRaises(ValueError): + num_val.save() def test_decimal_update(self): """ diff --git a/tests/system/django_spanner/utils.py b/tests/system/django_spanner/utils.py index 7fac5166e0..3dca9db9b8 100644 --- a/tests/system/django_spanner/utils.py +++ b/tests/system/django_spanner/utils.py @@ -15,11 +15,12 @@ from test_utils.retry import RetryErrors from django_spanner.creation import DatabaseCreation +from django_spanner import USE_EMULATOR CREATE_INSTANCE = ( os.getenv("GOOGLE_CLOUD_TESTS_CREATE_SPANNER_INSTANCE") is not None ) -USE_EMULATOR = os.getenv("SPANNER_EMULATOR_HOST") is not None + SPANNER_OPERATION_TIMEOUT_IN_SECONDS = int( os.getenv("SPANNER_OPERATION_TIMEOUT_IN_SECONDS", 60) ) diff --git a/version.py b/version.py index df949c65d1..32ec82411a 100644 --- a/version.py +++ b/version.py @@ -4,4 +4,4 @@ # license that can be found in the LICENSE file or at # https://developers.google.com/open-source/licenses/bsd -__version__ = "2.2.1b2" +__version__ = "2.2.1b4"