Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9272864
OSF to use latest djelme
bodintsov Mar 20, 2026
590f7a2
fix test fails
bodintsov Mar 23, 2026
6ebdd8e
fix poetry issue
bodintsov Mar 23, 2026
fbe2a08
add connection
bodintsov Mar 23, 2026
ea78a15
remove connection, add proper setUp and tearDown
bodintsov Mar 24, 2026
0efd0b1
remove elasticsearch and elasticsearch-dsl
bodintsov Mar 25, 2026
684a83f
remove elasticsearch-dsl
bodintsov Mar 25, 2026
1151610
remove sleep() and refresh indices
bodintsov Mar 25, 2026
4649800
remove unused imports, comment out
bodintsov Mar 25, 2026
5ea0ed1
chore: bump djelme dependency
aaxelb Mar 25, 2026
af0dfae
Merge pull request #11644 from bodintsov/feature/add-djelme
aaxelb Mar 26, 2026
674f963
wip: es8 djelme records (migration targets)
aaxelb Mar 25, 2026
2e73161
add new metrics
bodintsov Mar 31, 2026
4b4a478
fix flake8
bodintsov Apr 6, 2026
d3b48e4
add tests, use new version of djelme, consolidate into OsfCountedUsag…
bodintsov Apr 8, 2026
e4bec9d
add imports to init, flake8
bodintsov Apr 9, 2026
ee515ef
fix test, imports, flake8
bodintsov Apr 9, 2026
ca60b58
add security, flake8, fixes, add to test-build.yml
bodintsov Apr 10, 2026
080daf6
test-build update
bodintsov Apr 10, 2026
fde32a4
test-build fix url
bodintsov Apr 10, 2026
e6da70b
test-build fix naming
bodintsov Apr 10, 2026
2b8a81c
update test
bodintsov Apr 11, 2026
6167778
add wait
bodintsov Apr 13, 2026
eb0a5d9
remove wait
bodintsov Apr 13, 2026
78ed96f
cleanup
bodintsov Apr 14, 2026
70cf5e2
add wait, downgrade djelme, flake8
bodintsov Apr 14, 2026
3e35fee
add elastic8
bodintsov Apr 14, 2026
a236342
fix test
bodintsov Apr 14, 2026
00b055b
timedepth constants
aaxelb Apr 14, 2026
dddc94e
tidy gh actions with yaml anchors, health checks
aaxelb Apr 14, 2026
46a934f
simplify local elasticsearch8 config
aaxelb Apr 14, 2026
49f9259
bump djelme to get fixes
aaxelb Apr 14, 2026
29839b9
tests passing with djelme es8
aaxelb Apr 14, 2026
619cac7
fix(test): patch check_index_template
aaxelb Apr 14, 2026
8cec095
uncomment autouse fixture
aaxelb Apr 14, 2026
c24430f
remove unnecessary loop
aaxelb Apr 14, 2026
cd32827
plac8 flake8
aaxelb Apr 14, 2026
db938be
remove unused local env vars
aaxelb Apr 14, 2026
52a2bc9
better use waffle switch ELASTICSEARCH_METRICS
aaxelb Apr 14, 2026
82de65b
mock check mock save
aaxelb Apr 14, 2026
b33280d
remove the override
bodintsov Apr 15, 2026
1cef7d3
fix failing test
bodintsov Apr 15, 2026
6c45a66
Merge pull request #11672 from bodintsov/feature/add-new-es8-metrics
aaxelb Apr 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .docker-compose.env
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ INTERNAL_DOMAIN=http://192.168.168.167:5000/
API_DOMAIN=http://localhost:8000/
ELASTIC_URI=192.168.168.167:9200
ELASTIC6_URI=192.168.168.167:9201
ELASTIC8_URI=http://192.168.168.167:9202
ELASTIC8_USERNAME=elastic
OSF_DB_HOST=192.168.168.167
DB_HOST=192.168.168.167
REDIS_HOST=redis://192.168.168.167:6379
Expand Down
103 changes: 27 additions & 76 deletions .github/workflows/test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,19 @@ jobs:
permissions:
checks: write
services:
postgres:
elasticsearch8: &ES8_SERVICE
image: elasticsearch:8.19.14
ports:
- 9202:9200
env:
discovery.type: single-node
xpack.security.enabled: false
options: >-
--health-cmd "curl -sf http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=30s"
--health-interval 10s
--health-timeout 30s
--health-retries 5
postgres: &POSTGRES_SERVICE
image: postgres
env:
POSTGRES_PASSWORD: ${{ env.OSF_DB_PASSWORD }}
Expand All @@ -54,6 +66,8 @@ jobs:
- uses: ./.github/actions/start-build
- name: Run tests
run: poetry run python3 -m invoke test-ci-addons --junit
env:
ELASTIC8_URI: http://localhost:9202
- name: Upload report
if: (success() || failure()) # run this step even if previous step failed
uses: ./.github/actions/gen-report
Expand All @@ -64,18 +78,7 @@ jobs:
permissions:
checks: write
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: ${{ env.OSF_DB_PASSWORD }}
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
postgres: *POSTGRES_SERVICE
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/start-build
Expand All @@ -91,25 +94,17 @@ jobs:
permissions:
checks: write
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: ${{ env.OSF_DB_PASSWORD }}
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
elasticsearch8: *ES8_SERVICE
postgres: *POSTGRES_SERVICE
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/start-build
- name: NVM & yarn install
run: poetry run python3 -m invoke assets --dev
- name: Run test
run: poetry run python3 -m invoke test-ci-api1-and-js --junit
env:
ELASTIC8_URI: http://localhost:9202
- name: Upload report
if: (success() || failure()) # run this step even if previous step failed
uses: ./.github/actions/gen-report
Expand All @@ -120,23 +115,15 @@ jobs:
permissions:
checks: write
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: ${{ env.OSF_DB_PASSWORD }}
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
elasticsearch8: *ES8_SERVICE
postgres: *POSTGRES_SERVICE
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/start-build
- name: Run tests
run: poetry run python3 -m invoke test-ci-api2 --junit
env:
ELASTIC8_URI: http://localhost:9202
- name: Upload report
if: (success() || failure()) # run this step even if previous step failed
uses: ./.github/actions/gen-report
Expand All @@ -147,19 +134,7 @@ jobs:
checks: write
needs: build-cache
services:
postgres:
image: postgres

env:
POSTGRES_PASSWORD: ${{ env.OSF_DB_PASSWORD }}
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
postgres: *POSTGRES_SERVICE
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/start-build
Expand All @@ -175,19 +150,7 @@ jobs:
checks: write
needs: build-cache
services:
postgres:
image: postgres

env:
POSTGRES_PASSWORD: ${{ env.OSF_DB_PASSWORD }}
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
postgres: *POSTGRES_SERVICE
mailhog:
image: mailhog/mailhog
ports:
Expand All @@ -208,19 +171,7 @@ jobs:
checks: write
needs: build-cache
services:
postgres:
image: postgres

env:
POSTGRES_PASSWORD: ${{ env.OSF_DB_PASSWORD }}
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
postgres: *POSTGRES_SERVICE
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/start-build
Expand Down
2 changes: 1 addition & 1 deletion addons/base/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import waffle
from django.db import transaction
from django.contrib.contenttypes.models import ContentType
from elasticsearch import exceptions as es_exceptions
from elasticsearch6 import exceptions as es_exceptions
from rest_framework import status as http_status

from api.caching.tasks import update_storage_usage_with_size
Expand Down
8 changes: 4 additions & 4 deletions api/base/elasticsearch_dsl_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import datetime
import typing

import elasticsearch_dsl as edsl
import elasticsearch6_dsl as edsl
from rest_framework import generics, exceptions as drf_exceptions
from rest_framework.settings import api_settings as drf_settings
from api.base.settings.defaults import REPORT_FILENAME_FORMAT
Expand All @@ -23,7 +23,7 @@


class ElasticsearchListView(FilterMixin, JSONAPIBaseView, generics.ListAPIView, abc.ABC):
'''abstract view class using `elasticsearch_dsl.Search` as a queryset-analogue
'''abstract view class using `elasticsearch6_dsl.Search` as a queryset-analogue

builds a `Search` based on `self.get_default_search()` and the request's
query parameters for filtering, sorting, and pagination -- fetches only
Expand All @@ -36,7 +36,7 @@ class ElasticsearchListView(FilterMixin, JSONAPIBaseView, generics.ListAPIView,

@abc.abstractmethod
def get_default_search(self) -> edsl.Search | None:
'''the base `elasticsearch_dsl.Search` for this list, based on url path
'''the base `elasticsearch6_dsl.Search` for this list, based on url path

(common jsonapi query parameters will be considered automatically)
'''
Expand Down Expand Up @@ -95,7 +95,7 @@ def finalize_response(self, request, response, *args, **kwargs):
# (filtering handled in-view to reuse logic from FilterMixin)
filter_backends = ()

# note: because elasticsearch_dsl.Search supports slicing and gives results when iterated on,
# note: because elasticsearch6_dsl.Search supports slicing and gives results when iterated on,
# it works fine with default pagination

# override rest_framework.generics.GenericAPIView
Expand Down
21 changes: 17 additions & 4 deletions api/base/settings/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,23 @@
HASHIDS_SALT = 'pinkhimalayan'

# django-elasticsearch-metrics
ELASTICSEARCH_DSL = {
'default': {
'hosts': osf_settings.ELASTIC6_URI,
'retry_on_timeout': True,
DJELME_BACKENDS = {
'osfmetrics_es6': {
'elasticsearch_metrics.imps.elastic6': {
'hosts': osf_settings.ELASTIC6_URI,
'retry_on_timeout': True,
},
},
'osfmetrics_es8': {
'elasticsearch_metrics.imps.elastic8': {
'hosts': osf_settings.ELASTIC8_URI,
'ca_certs': osf_settings.ELASTIC8_CERT_PATH,
'basic_auth': (
(osf_settings.ELASTIC8_USERNAME, osf_settings.ELASTIC8_SECRET)
if osf_settings.ELASTIC8_SECRET is not None
else None
),
},
},
}
# Store yearly indices for time-series metrics
Expand Down
4 changes: 2 additions & 2 deletions api/metrics/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from django.http import JsonResponse, HttpResponse, Http404
from django.utils import timezone

from elasticsearch.exceptions import NotFoundError, RequestError
from elasticsearch_dsl.connections import get_connection
from elasticsearch6.exceptions import NotFoundError, RequestError
from elasticsearch6_dsl.connections import get_connection

from framework.auth.oauth_scopes import CoreScopes

Expand Down
11 changes: 6 additions & 5 deletions api_tests/institutions/views/test_institution_department_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def populate_counts(self, user, user2, user3, user4, admin, institution):
department_name='Old Department',
public_project_count=1,
private_project_count=1,
).save(refresh=True)
).save()

_this_month = YearMonth.from_date(datetime.date.today())

Expand All @@ -56,7 +56,7 @@ def populate_counts(self, user, user2, user3, user4, admin, institution):
department_name='New Department',
public_project_count=1,
private_project_count=1,
).save(refresh=True)
).save()

# A second user entered the department
InstitutionalUserReport(
Expand All @@ -66,7 +66,7 @@ def populate_counts(self, user, user2, user3, user4, admin, institution):
department_name='New Department',
public_project_count=1,
private_project_count=1,
).save(refresh=True)
).save()

# A new department with a single user to test sorting
InstitutionalUserReport(
Expand All @@ -76,7 +76,7 @@ def populate_counts(self, user, user2, user3, user4, admin, institution):
department_name='Smaller Department',
public_project_count=1,
private_project_count=1,
).save(refresh=True)
).save()

# A user with no department
InstitutionalUserReport(
Expand All @@ -85,7 +85,7 @@ def populate_counts(self, user, user2, user3, user4, admin, institution):
institution_id=institution._id,
public_project_count=1,
private_project_count=1,
).save(refresh=True)
).save()

@pytest.fixture()
def admin(self, institution):
Expand Down Expand Up @@ -113,6 +113,7 @@ def test_auth(self, app, url, user, admin):
assert resp.json['data'] == []

def test_get(self, app, url, admin, institution, populate_counts):
InstitutionalUserReport._get_connection().indices.refresh(InstitutionalUserReport._template_pattern)
resp = app.get(url, auth=admin.auth)

assert resp.json['data'] == [{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def test_get_empty(self, app, url, institutional_admin):
assert resp.json['meta'] == {'version': '2.0'}

def test_get_report(self, app, url, institutional_admin, institution, reports, unshown_reports):
InstitutionMonthlySummaryReport._get_connection().indices.refresh(InstitutionMonthlySummaryReport._template_pattern)
resp = app.get(url, auth=institutional_admin.auth)
assert resp.status_code == 200

Expand Down Expand Up @@ -149,6 +150,7 @@ def test_get_report_with_multiple_months_and_institutions(
monthly_logged_in_user_count=270,
monthly_active_user_count=260,
)
InstitutionMonthlySummaryReport._get_connection().indices.refresh(InstitutionMonthlySummaryReport._template_pattern)

resp = app.get(url, auth=institutional_admin.auth)
assert resp.status_code == 200
Expand Down Expand Up @@ -189,6 +191,7 @@ def test_get_with_valid_report_dates(self, app, url, institution, institutional_
institution,
user_count=4133,
)
InstitutionMonthlySummaryReport._get_connection().indices.refresh(InstitutionMonthlySummaryReport._template_pattern)

resp = app.get(f'{url}?report_yearmonth=2024-08', auth=institutional_admin.auth)
assert resp.status_code == 200
Expand All @@ -213,6 +216,7 @@ def test_get_with_invalid_report_date(self, app, url, institution, institutional
institution,
user_count=999,
)
InstitutionMonthlySummaryReport._get_connection().indices.refresh(InstitutionMonthlySummaryReport._template_pattern)

# Request with an invalid report_date format
resp = app.get(f'{url}?report_yearmonth=invalid-date', auth=institutional_admin.auth)
Expand All @@ -233,6 +237,7 @@ def test_get_without_report_date_uses_most_recent(self, app, url, institution, i
institution,
user_count=999,
)
InstitutionMonthlySummaryReport._get_connection().indices.refresh(InstitutionMonthlySummaryReport._template_pattern)

resp = app.get(url, auth=institutional_admin.auth)
assert resp.status_code == 200
Expand All @@ -247,5 +252,5 @@ def _summary_report_factory(yearmonth, institution, **kwargs):
institution_id=institution._id,
**kwargs,
)
report.save(refresh=True)
report.save()
return report
Loading
Loading