diff --git a/.gitignore b/.gitignore index 923c74121381a8d77989b2b92582383d8a0f0c3b..d6afba32a2763b78f099a6e2001f806c50277060 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ core /.run/_* .envrc.local terraform*.log +/.gitlab-ci-local diff --git a/EDITING_INSIGHTS.md b/EDITING_INSIGHTS.md index 6b84ccb6a4f0b70e04832d856f4956ae11c4dea8..e0f4d221f32c83231f66152a110937f05aa40b53 100644 --- a/EDITING_INSIGHTS.md +++ b/EDITING_INSIGHTS.md @@ -4,21 +4,11 @@ You can edit insights via the admin interface that Django provides. ## Editing insights on Production (and Staging) -All okuna deployments in the cloud are not open to the internet. Therefore, we need to do a little work in order to -connect to it. - ### Using reverse proxy -https://0kuna-adm1n.holi.social/admin currently provides the admin interface of the branch. We should switch this to production later. And provide access to staging as well via another domain. - -## Setting up editing insights - -### reverse proxy - -The reverse proxy is set up manually on the hop host. The hop host also is created manually. It has a public IPv4 and costs around 10€/month when running 24/7, 4€/month when stopped -in between usages. We need to decide how to provide this service. +https://0kuna-adm1n.holi.social/production/admin provides the admin interface for production, https://0kuna-adm1n.holi.social/staging/admin for the staging/main branch. -For now I have created a schedule on which the VM starts +## Protocol of setting up editing insights ### Creating Superuser in DB @@ -44,4 +34,4 @@ And then I could execute (in the project directory): echo "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('admin', 'admin@myproject.com', 'password')" | python manage.py shell ``` -(from https://stackoverflow.com/questions/6244382/how-to-automate-createsuperuser-on-django) \ No newline at end of file +(from https://stackoverflow.com/questions/6244382/how-to-automate-createsuperuser-on-django) diff --git a/comments/admin.py b/comments/admin.py index 082964b80ea5e7bc05cf4119f1fedbf30dcd1623..14ca80b51dc1e0df6e042e31a0c6229bf03b7858 100644 --- a/comments/admin.py +++ b/comments/admin.py @@ -1,4 +1,7 @@ from django.contrib import admin +from django.contrib.contenttypes.models import ContentType +from django.urls import reverse +from django.utils.html import format_html from rangefilter.filters import DateRangeQuickSelectListFilterBuilder from .models import Comment @@ -13,13 +16,27 @@ class PostCommentAdmin(admin.ModelAdmin): "text", "created", "commenter", + "link_to_commentable", "language_code", "count_replies", "count_reactions", "count_mentions", "count_links", "is_deleted", - "commentable", ) list_filter = (("created", DateRangeQuickSelectListFilterBuilder(title="Created")),) search_fields = ("text", "commenter") + ordering = ("-created",) + + def link_to_commentable(self, obj): + content_type = ContentType.objects.get_for_model(model=obj.commentable_type.model_class()) + link = reverse("admin:%s_%s_change" % (content_type.app_label, content_type.model), args=(obj.commentable_id,)) + commentable_content_type = ContentType.objects.get_for_model(model=obj.commentable_type.model_class()) + commentable = commentable_content_type.get_object_for_this_type(pk=obj.commentable_id) + return format_html( + '<a href="{}">{}</a>', + link, + commentable, + ) + + link_to_commentable.short_description = "Commentable" # new diff --git a/okuna-cli.py b/okuna-cli.py index 3afb139adc326c3a7f83313a885d8908a94ee604..9ca7322134a00444c9279ff04ed48ba1716ff4fc 100755 --- a/okuna-cli.py +++ b/okuna-cli.py @@ -57,7 +57,7 @@ def _remove_file_silently(filename): def _generate_random_string( length=12, - allowed_chars="abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + allowed_chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", ): """ Return a securely generated random string. diff --git a/openbook_auth/tasks.py b/openbook_auth/tasks.py index 92918cd1fefc152a9b9620a389e4c26d20416c42..6e0c3c77dcd163e0e27c0dda03ad469e96cf6751 100644 --- a/openbook_auth/tasks.py +++ b/openbook_auth/tasks.py @@ -53,6 +53,7 @@ def update_ory_user_identity(user: User) -> bool: def update_ory(user_id, data): + # TODO: handle requests.exceptions.Timeout response = requests.patch( f"{settings.ORY_BASE_URL}/admin/identities/{user_id}", headers=ORY_HEADERS, json=data, timeout=30 ) diff --git a/openbook_common/management/commands/worker_health_check.py b/openbook_common/management/commands/worker_health_check.py index 4261a19dbe175796406ffc1c5b6e980fe5f35852..7c86f8c9df67e1643ac494881f56fe5cda9353cd 100644 --- a/openbook_common/management/commands/worker_health_check.py +++ b/openbook_common/management/commands/worker_health_check.py @@ -30,7 +30,7 @@ class Command(BaseCommand): if active_job_count >= ACTIVE_JOB_THRESHOLD: send_alert_to_channel( - f"*UH-OH: we have way too many active jobs " f"in {env}:{queue} right now: {active_job_count}!!*" + f"*UH-OH: we have way too many active jobs in {env}:{queue} right now: {active_job_count}!!*" ) print(f"{queue} has too many jobs {active_job_count}") self.retval += 1 @@ -39,7 +39,7 @@ class Command(BaseCommand): if active_worker_count >= ACTIVE_WORKER_THRESHOLD: send_alert_to_channel( - f"*Hmm, we are not supposed to have " f"{active_worker_count} workers in " f"{env}:{queue}*" + f"*Hmm, we are not supposed to have {active_worker_count} workers in {env}:{queue}*" ) print(f"{queue} has too many workers {active_worker_count}") diff --git a/openbook_common/serializers.py b/openbook_common/serializers.py index 6475eb6d314c5627b2cd98833e403939bf572bb9..f6311946a41914a55fcaa5822166eef0fb03d646 100644 --- a/openbook_common/serializers.py +++ b/openbook_common/serializers.py @@ -339,7 +339,7 @@ class GeoFeatureModelSerializer(serializers.ModelSerializer): meta.auto_bbox = getattr(meta, "auto_bbox", False) if meta.bbox_geo_field and meta.auto_bbox: raise ImproperlyConfigured( - "You must eiher define a 'bbox_geo_field' or " "'auto_bbox', but you can not set both" + "You must either define a 'bbox_geo_field' or 'auto_bbox', but you can not set both" ) def to_representation(self, instance): diff --git a/openbook_common/tests/helpers.py b/openbook_common/tests/helpers.py index 426fdef80930649534b55ae1ecaee1004c5ae2d3..a8c076aa91a699a0905ab457666c268009fafeec 100644 --- a/openbook_common/tests/helpers.py +++ b/openbook_common/tests/helpers.py @@ -92,7 +92,7 @@ def make_user( defined_arguments["id"] = defined_arguments["username"] user = mixer.blend(User, **defined_arguments) - profile = make_profile(user, **defined_arguments) + profile = make_profile(user, name=name, last_name=last_name, engagement_level=engagement_level) if user.profile.full_name() and not user.identity: user.identity = User.sanitize_identity_input(user.profile.full_name()) user.save() @@ -121,7 +121,7 @@ def make_profile( user: Optional[User] = None, name: Optional[str] = None, last_name: Optional[str] = None, - **kwargs, + engagement_level=None, ) -> UserProfile: defined_arguments = {k: v for k, v in locals().items() if v is not None} return mixer.blend(UserProfile, **defined_arguments) diff --git a/openbook_communities/admin.py b/openbook_communities/admin.py index 32d7ac224164f784e44eab3ebf9a08c7d7fc1740..3a7e8943f636f2d1d50f8f2dd4754a24ef8b240c 100644 --- a/openbook_communities/admin.py +++ b/openbook_communities/admin.py @@ -94,7 +94,7 @@ class CommunityAdmin(admin.ModelAdmin): def formfield_for_foreignkey(self, db_field, request, **kwargs): def nice_name(obj): - return f'{obj.profile.name if hasattr(obj, "profile") else ""} {obj.profile.last_name if hasattr(obj, "profile") else ""} ({obj.email}), {obj.username}' + return f"{obj.profile.name if hasattr(obj, 'profile') else ''} {obj.profile.last_name if hasattr(obj, 'profile') else ''} ({obj.email}), {obj.username}" formfield = super().formfield_for_foreignkey(db_field, request, **kwargs) if db_field.name == "creator": diff --git a/openbook_communities/helpers.py b/openbook_communities/helpers.py index 8e413d1ba20f1dc442b20d8866be3638b4199fe6..a1c56542b58980c5d7aeacee1c709127026dc5a5 100644 --- a/openbook_communities/helpers.py +++ b/openbook_communities/helpers.py @@ -59,9 +59,7 @@ def create_community_name_from_title_and_hash(title, hash): except ValidationError: name_suffix_length = min(8, len(hash)) while name_suffix_length <= len(hash) and name_suffix_length > 0: - name = ( - f"{slug[: settings.COMMUNITY_NAME_MAX_LENGTH - name_suffix_length - 1]}-{hash[: name_suffix_length]}" - ) + name = f"{slug[: settings.COMMUNITY_NAME_MAX_LENGTH - name_suffix_length - 1]}-{hash[:name_suffix_length]}" try: community_name_not_taken_validator(name) return name diff --git a/openbook_communities/tests/test_graphql_external_links.py b/openbook_communities/tests/test_graphql_external_links.py index 9755034a25882b3eaedbc217510f18f12e7334fc..87c2b1316ad3e8fda8ba765c23716cac940f72b6 100644 --- a/openbook_communities/tests/test_graphql_external_links.py +++ b/openbook_communities/tests/test_graphql_external_links.py @@ -120,9 +120,9 @@ class TestExternalLinks: if "expected_code" in testCase: field_errors = json.loads(response.errors[0].extensions["errors"]) - assert ( - testCase["expected_code"] == field_errors[testCase["field"]][0]["code"] - ), f"{str(response.errors[0].extensions)} does not contain code: {testCase['expected_code']} from testCase: {testCase}" + assert testCase["expected_code"] == field_errors[testCase["field"]][0]["code"], ( + f"{str(response.errors[0].extensions)} does not contain code: {testCase['expected_code']} from testCase: {testCase}" + ) else: assert testCase["expected_error"] in str(response.errors[0].message) assert (response.data["addExternalLink"] if response.data else response.data) is None @@ -346,9 +346,9 @@ class TestExternalLinks: if "expected_code" in testCase: field_errors = json.loads(response.errors[0].extensions["errors"]) - assert ( - testCase["expected_code"] == field_errors[testCase["field"]][0]["code"] - ), f"{str(response.errors[0].extensions)} does not contain code: {testCase['expected_code']} from testCase: {testCase}" + assert testCase["expected_code"] == field_errors[testCase["field"]][0]["code"], ( + f"{str(response.errors[0].extensions)} does not contain code: {testCase['expected_code']} from testCase: {testCase}" + ) else: assert testCase["expected_error"] in str(response.errors[0].message) assert (response.data["updateExternalLink"] if response.data else response.data) is None diff --git a/openbook_invitations/management/commands/reset_invite_email_boolean.py b/openbook_invitations/management/commands/reset_invite_email_boolean.py index 7c7afb69c2dfbcb9e108d50ad0affb5dfed65125..6f1acbd4f90727f3b61c85e22743f7e812234a30 100644 --- a/openbook_invitations/management/commands/reset_invite_email_boolean.py +++ b/openbook_invitations/management/commands/reset_invite_email_boolean.py @@ -15,7 +15,7 @@ class Command(BaseCommand): parser.add_argument( "--days", type=str, - help="Invites going back these many days will have their boolean" " reset if no user is created", + help="Invites going back these many days will have their boolean reset if no user is created", ) def handle(self, *args, **options): diff --git a/openbook_notifications/payloads.py b/openbook_notifications/payloads.py index 528969075a1de12deda1818adffb9ee37e377dfd..a1ee4c355439a6c6327839ee397480351b0e15ff 100644 --- a/openbook_notifications/payloads.py +++ b/openbook_notifications/payloads.py @@ -619,7 +619,7 @@ class WelcomeEmailSeriesPayload(Payload): "user": UserData.from_user(user).to_dict(), "recommendedSpaces": [SpaceData.from_community(space).to_dict() for space in spaces], "recommendedInsights": [InsightData.from_insight(insight).to_dict() for insight in insights], - "token": token, + "unsubscribeUrl": f"{app_url}/unsubscribe?series=welcome&email={user.email}&token={token}", }, notification_type=NotificationType.WELCOME_EMAIL_SERIES.value, notification_version=WelcomeEmailSeriesPayload.NOTIFICATION_VERSION, diff --git a/requirements-cli-only.txt b/requirements-cli-only.txt index 3a4a4b8b424c850726b8c88f9ed9fc0ff26f2e7b..fa1eb6a468247bb3574261e54051f3cf306b22a0 100644 --- a/requirements-cli-only.txt +++ b/requirements-cli-only.txt @@ -1,5 +1,4 @@ -i https://pypi.python.org/simple -click==8.1.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -requests==2.28.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -halo==0.0.31 -colorlog==6.7.0 +click>=8.1.8 +halo>=0.0.31 +colorlog>=6.9.0 diff --git a/requirements.txt b/requirements.txt index 100c827dd8a23265fbc0d75960dc0f0e38f43c64..e1c992e88eb55e5c29eb23f0b14c3fed285906b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,144 +1,140 @@ # For upgrading all dependencies to their highest compatible versions: # -# sed -i 's/[~=]=/>=/' requirements.txt # replace all version specifiers with a companion specifier -# pip install -U -r requirements.txt # now upgrade all packages in requirements.txt -# pip freeze | sed 's/==/~=/' > requirements.txt # freeze adds any transient dependencies and makes them explicit -# # then, we transform the exact matches to compatibility matches +# sed -i 's/[~=]=/>=/' requirements.txt # replace all version specifiers with a companion specifier +# pip install -U -r requirements.txt # now upgrade all packages in requirements.txt +# pip freeze # freeze adds any transient dependencies and makes them explicit # # Afterwards, some packages might need a downgrade or version tweak for the bundle fitting together and fitting the code -adrf~=0.1.8 -aiofiles~=24.1.0 -aiohappyeyeballs~=2.4.3 -aiohttp~=3.11.11 -aiosignal~=1.3.1 -ASGIMiddlewareStaticFile~=0.6.1 -asgiref~=3.8.1 -async-property~=0.2.2 -attrs~=24.3.0 -backoff~=2.2.1 -beautifulsoup4~=4.13.0 -black~=24.10.0 -blurhash-python~=1.2.2 -cachetools~=5.5.0 -certifi~=2024.12.14 -cffi~=1.17.1 -charset-normalizer~=3.4.0 -click~=8.1.7 -colorama~=0.4.6 -colorlog~=6.9.0 -coverage~=7.6.4 -Deprecated~=1.2.14 -Django~=5.0.7 -django-admin-rangefilter~=0.13.2 -django-appconf~=1.0.6 -django-cacheops~=7.1.0 -django-cors-headers~=4.6.0 -django-cursor-pagination~=0.3.0 -django-debug-toolbar~=4.4.6 -django-extensions~=3.2.3 -django-imagekit~=5.0.0 -django-ipware~=7.0.1 -django-modeltranslation~=0.19.10 -django-ordered-model~=3.7.4 -django-proxy~=1.3.0 -django-redis~=5.4.0 -django-rq~=3.0.0 -django-sortedm2m~=4.0.0 -django-structlog~=8.1.0 -djangorestframework~=3.15.2 -djangorestframework-camel-case~=1.4.2 -execnet~=2.1.1 -Faker~=12.0.1 # mixer 7.2.2 depends on Faker<12.1 and >=5.4.0 -filelock~=3.17.0 -frozenlist~=1.5.0 -funcy~=2.0 -google-api-core~=2.24.1 -google-auth~=2.38.0 -google-cloud-pubsub~=2.28.0 -google-cloud-webrisk~=1.16.0 -googleapis-common-protos~=1.66.0 -graphql-core~=3.2.5 -grpc-google-iam-v1~=0.13.1 -grpcio~=1.70.0 -grpcio-status~=1.70.0 -h11~=0.14.0 -halo~=0.0.31 -hiredis~=3.1.0 -icalendar~=6.1.1 -idna~=3.10 +adrf==0.1.9 +aiofiles==24.1.0 +aiohappyeyeballs==2.4.6 +aiohttp==3.11.13 +aiosignal==1.3.2 +ASGIMiddlewareStaticFile==0.6.1 +asgiref==3.8.1 +async-property==0.2.2 +attrs==25.1.0 +backoff==2.2.1 +beautifulsoup4==4.13.3 +black==25.1.0 +blurhash-python==1.2.2 +cachetools==5.5.2 +certifi==2025.1.31 +cffi==1.17.1 +charset-normalizer==3.4.1 +colorama==0.4.6 +coverage==7.6.12 +Deprecated==1.2.18 +Django==5.0.12 +django-admin-rangefilter==0.13.2 +django-appconf==1.1.0 +django-cacheops==7.1 +django-cors-headers==4.7.0 +django-cursor-pagination==0.3.0 +django-debug-toolbar==5.0.1 +django-extensions==3.2.3 +django-imagekit==5.0.0 +django-ipware==7.0.1 +django-modeltranslation==0.19.12 +django-ordered-model==3.7.4 +django-proxy==1.3.0 +django-redis==5.4.0 +django-rq==3.0.0 +django-sortedm2m==4.0.0 +django-structlog==9.0.1 +djangorestframework==3.15.2 +djangorestframework-camel-case==1.4.2 +execnet==2.1.1 +Faker==12.0.1 # mixer 7.2.2 depends on Faker<12.1 and >=5.4.0 +filelock==3.17.0 +frozenlist==1.5.0 +funcy==2.0 +google-api-core==2.24.1 +google-auth==2.38.0 +google-cloud-pubsub==2.28.0 +google-cloud-webrisk==1.17.0 +googleapis-common-protos==1.68.0 +graphql-core==3.2.6 +grpc-google-iam-v1==0.14.0 +grpcio==1.70.0 +grpcio-status==1.70.0 +h11==0.14.0 +hiredis==3.1.0 +icalendar==6.1.1 +idna==3.10 imagekitio==2.2.8 # version 3 contains many breaking changes -importlib_metadata~=8.4.0 -iniconfig~=2.0.0 -Jinja2~=3.1.4 -langdetect~=1.0.9 -log-symbols~=0.0.14 -MarkupSafe~=3.0.2 -mixer~=7.2.2 -monotonic~=1.6 -multidict~=6.1.0 -mypy-extensions~=1.0.0 -novu~=1.14.0 -opentelemetry-api~=1.27.0 -opentelemetry-sdk~=1.27.0 -opentelemetry-semantic-conventions~=0.48b0 -packaging~=24.1 -pathspec~=0.12.1 -pilkit~=3.0 -pillow~=11.1.0 -platformdirs~=4.3.6 -pluggy~=1.5.0 -posthog~=3.11.0 -propcache~=0.2.0 -proto-plus~=1.26.0 -protobuf~=5.29.3 -psycopg~=3.2.3 -psycopg-binary~=3.2.3 -pyasn1~=0.6.1 -pyasn1_modules~=0.4.1 -pycparser~=2.22 -PyJWT~=2.10.1 -pytest~=8.3.3 -pytest-asyncio~=0.24.0 -pytest-cov~=6.0.0 -pytest-django~=4.9.0 -pytest-xdist~=3.6.1 -python-benedict~=0.34.0 -python-dateutil~=2.9.0.post0 -python-dotenv~=1.0.1 -python-fsutil~=0.14.1 -python-ipware~=3.0.0 -python-magic~=0.4.27 -python-slugify~=8.0.4 -pytz~=2024.2 -redis~=5.2.0 -requests~=2.32.3 -requests-file~=2.1.0 -requests-toolbelt~=1.0.0 -rest-framework-generic-relations~=2.2.0 -rq~=2.1.0 -rsa~=4.9 -ruamel.yaml~=0.18.6 -ruamel.yaml.clib~=0.2.12 -ruff~=0.7.1 -sentry-sdk~=2.20.0 -six~=1.17.0 -soupsieve~=2.6 -spinners~=0.0.24 -sqlparse~=0.5.1 -strawberry-graphql~=0.247.0 -strawberry-graphql-django~=0.49.1 -structlog~=24.4.0 -termcolor~=2.5.0 -text-unidecode~=1.3 -tldextract~=5.1.2 -typing_extensions~=4.12.2 -tzdata~=2024.2 -Unidecode~=1.3.8 -uritools~=4.0.3 -url-normalize~=1.4.3 -urlextract~=1.9.0 -urllib3~=2.3.0 -uvicorn~=0.32.0 -wrapt~=1.16.0 -yarl~=1.17.1 -zipp~=3.20.2 +importlib_metadata==8.5.0 +iniconfig==2.0.0 +Jinja2==3.1.5 +langdetect==1.0.9 +log-symbols==0.0.14 +MarkupSafe==3.0.2 +mixer==7.2.2 +monotonic==1.6 +multidict==6.1.0 +mypy-extensions==1.0.0 +novu==1.14.0 +opentelemetry-api==1.30.0 +opentelemetry-sdk==1.30.0 +opentelemetry-semantic-conventions==0.51b0 +packaging==24.2 +pathspec==0.12.1 +pilkit==3.0 +pillow==11.1.0 +platformdirs==4.3.6 +pluggy==1.5.0 +posthog==3.15.1 +propcache==0.3.0 +proto-plus==1.26.0 +protobuf==5.29.3 +psycopg==3.2.5 +psycopg-binary==3.2.5 +pyasn1==0.6.1 +pyasn1_modules==0.4.1 +pycparser==2.22 +PyJWT==2.10.1 +pytest==8.3.4 +pytest-asyncio==0.25.3 +pytest-cov==6.0.0 +pytest-django==4.10.0 +pytest-xdist==3.6.1 +python-benedict==0.34.1 +python-dateutil==2.9.0.post0 +python-dotenv==1.0.1 +python-fsutil==0.15.0 +python-ipware==3.0.0 +python-magic==0.4.27 +python-slugify==8.0.4 +pytz==2025.1 +redis==5.2.1 +requests==2.32.3 +requests-file==2.1.0 +requests-toolbelt==1.0.0 +rest-framework-generic-relations==2.2.0 +rq==2.1.0 +rsa==4.9 +ruamel.yaml==0.18.10 +ruamel.yaml.clib==0.2.12 +ruff==0.9.7 +sentry-sdk==2.22.0 +six==1.17.0 +soupsieve==2.6 +spinners==0.0.24 +sqlparse==0.5.3 +strawberry-graphql==0.248.1 +strawberry-graphql-django==0.53.3 +structlog==25.1.0 +termcolor==2.5.0 +text-unidecode==1.3 +tldextract==5.1.3 +typing_extensions==4.12.2 +tzdata==2025.1 +Unidecode==1.3.8 +uritools==4.0.3 +url-normalize==1.4.3 +urlextract==1.9.0 +urllib3==2.3.0 +uvicorn==0.34.0 +wrapt==1.17.2 +yarl==1.18.3 +zipp==3.21.0 diff --git a/terraform/common/init.tf b/terraform/common/init.tf index 103aab65f38f49bed1efa1b25acf50407663eda4..7830c48f1a18dcec171e6f9252b12b70b8bba9ab 100644 --- a/terraform/common/init.tf +++ b/terraform/common/init.tf @@ -1,4 +1,16 @@ terraform { + # allow the lowest common version across all projects, so that the current CI docker image version suits all projects + required_version = ">= 1.9" + required_providers { + google = { + source = "hashicorp/google" + version = "6.22.0" + } + google-beta = { + source = "hashicorp/google-beta" + version = "6.21.0" + } + } backend "gcs" { bucket = "holi-shared-terraform-state" prefix = "okuna-common" diff --git a/terraform/environments/deployment.tf b/terraform/environments/deployment.tf index 1255610cf18d151f20fc01768175d4f5979a2796..7c9ff7ca9f906efcc49cd33a46b4fad587e3e7ec 100644 --- a/terraform/environments/deployment.tf +++ b/terraform/environments/deployment.tf @@ -421,6 +421,26 @@ resource "google_cloud_run_service" "okuna" { } } } + + startup_probe { + initial_delay_seconds = 10 + period_seconds = 5 + timeout_seconds = 5 + failure_threshold = 10 + http_get { + path = "/graphql?query=%7B__typename%7D" + } + } + + liveness_probe { + period_seconds = 15 + timeout_seconds = 10 + failure_threshold = 3 + http_get { + path = "/graphql?query=%7B__typename%7D" + } + } + resources { limits = { # cpu can only be scaled down to 1000m as long as container_concurrency is set to != 1 diff --git a/terraform/environments/init.tf b/terraform/environments/init.tf index 3bdf189408a60cc0aed2520e15c3f01200432ed4..98453d9f5244026a399ba0e1f20fab7ffd990190 100644 --- a/terraform/environments/init.tf +++ b/terraform/environments/init.tf @@ -1,4 +1,16 @@ terraform { + # allow the lowest common version across all projects, so that the current CI docker image version suits all projects + required_version = ">= 1.9" + required_providers { + google = { + source = "hashicorp/google" + version = "6.22.0" + } + google-beta = { + source = "hashicorp/google-beta" + version = "6.21.0" + } + } backend "gcs" { bucket = "holi-shared-terraform-state" prefix = "okuna-environments" diff --git a/terraform/environments/scripts/wait-for-ssl.sh b/terraform/environments/scripts/wait-for-ssl.sh index e130aadf5d8efde49192249d4b826ffd64a61ef6..2a53fa605f62bcdb379948e214c8718565f6cc51 100755 --- a/terraform/environments/scripts/wait-for-ssl.sh +++ b/terraform/environments/scripts/wait-for-ssl.sh @@ -7,7 +7,7 @@ url="$1" # google has a cdn answering on requests. This cdn takes a while to be fully updated. # Therefore, we don't return on first success, but on a number of consecutive successes. -number_of_consecutive_successful_tries_needed=10 +number_of_consecutive_successful_tries_needed=25 number_of_consecutive_successful_tries_achieved=0 [ -z "$url" ] && echo "missing url as first param" && exit 1 @@ -17,10 +17,21 @@ echo -n "Checking if SSL certificate for $url is installed: " # storage for the return value of the curl command retval=0 +# early break on success (no need to wait on existing deployments) +set +e +curl -sSLIm 10 "$url" > /dev/null 2>& 1 +retval=$? +set -e +if [ $retval -eq 0 ]; then + echo "success on first try, not checking further" + exit 0 +fi + + # shellcheck disable=SC2034 for i in {1..2500}; do set +e - out=$(curl -sSLI "$url" 2>&1) + out=$(curl -sSLIm 10 "$url" 2>& 1) retval=$? set -e # shellcheck disable=SC2181 @@ -31,6 +42,7 @@ for i in {1..2500}; do echo "test successful after $i total tries" exit 0 fi + sleep 1 else echo -n "." number_of_consecutive_successful_tries_achieved=0