[Fixes #14524] Remote-resource detection is inconsistent across harvesters - #14532
[Fixes #14524] Remote-resource detection is inconsistent across harvesters#14532etj wants to merge 21 commits into
Conversation
…ath in test_get_service_handler_wms
…andler Leftover from the earlier manual "Align to master" commit; harmless but redundant.
_get_service_handler (views.py) and GeoNodeServiceHandler.parsed_service
(wms.py) build the request auth via get_request_auth(), which returns a
HashableAuthBase-wrapped requests.auth object, but only passed auth= to
get_service_handler, not auth_config=. Since HashableAuthBase exposes no
`.username` attribute, _build_auth_cache_fingerprint fell through to a
fingerprint that was constant for every HashableAuthBase instance
("auth:HashableAuthBase"), so changing a service's stored credentials
would not bust the cached service handler until it naturally expired.
Fix both call sites to also pass auth_config=service.auth_config,
consistent with every other get_service_handler/get_service_cache_key
call site. Also harden _build_auth_cache_fingerprint itself to unwrap
HashableAuthBase and fingerprint the full underlying credential dict,
so the auth= path is correct on its own rather than relying solely on
callers always supplying auth_config too.
parsed_service calls self.ows_endpoint() (a real HTTP request) before get_cleaned_url_params, which wasn't mocked, causing a real DNS lookup against the phony test hostname. Mock ows_endpoint too.
There was a problem hiding this comment.
Pull request overview
This PR addresses inconsistencies in how GeoNode detects and handles remote resources across harvesters and service handlers, especially after allowing multiple Service records to share the same endpoint URL. It standardizes “remote-ness” on sourcetype, ensures harvested datasets don’t claim a GeoServer store, and makes service-handler caching service- and auth-specific.
Changes:
- Make remote-resource detection consistent by keying it on
sourcetype(and backfilling existing harvested resources via migration). - Relax remote service endpoint uniqueness (
Service.base_urlno longer unique) and make handler caching discriminated by(service_id, auth)to avoid collisions. - Ensure remote/harvested datasets have
store = NULL(andDataset.storebecomes nullable), preventing uniqueness conflicts and incorrect GeoServer assumptions.
Reviewed changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| geonode/utils.py | Passes service_id and auth_config to service handlers when creating default links. |
| geonode/services/views.py | Updates service-handler caching to use a service/auth-specific cache key; adjusts imports accordingly. |
| geonode/services/tests.py | Adds/updates tests for cache key stability, auth discrimination, and unique-name retry behavior. |
| geonode/services/serviceprocessors/wms.py | Ensures remote datasets have store=None, fixes auth resolution in GeoNodeServiceHandler.parsed_service, and uses unique-name creation helper. |
| geonode/services/serviceprocessors/cache.py | Introduces a dedicated cache wrapper for service handlers with auth-aware keying. |
| geonode/services/serviceprocessors/base.py | Adds helpers to generate unique names across Service and Harvester, retrying on IntegrityError. |
| geonode/services/serviceprocessors/arcgis.py | Aligns ArcGIS service creation with unique-name helper and sets remote dataset store=None. |
| geonode/services/serviceprocessors/init.py | Reworks handler caching to be keyed by service+auth and avoids caching when service_id is missing. |
| geonode/services/models.py | Removes uniqueness constraint from Service.base_url. |
| geonode/services/migrations/0061_alter_service_base_url.py | Migration to drop unique=True from Service.base_url. |
| geonode/services/forms.py | Removes duplicate-URL validation and updates Taggit field import usage. |
| geonode/security/tests.py | Adjusts expectations around metadata-only datasets; adds test ensuring remote resources skip GeoFence syncing. |
| geonode/resource/utils.py | Changes is_remote_resource() to rely on sourcetype (with fallback warning for legacy records). |
| geonode/layers/tests.py | Adds coverage for remote dataset uniqueness behavior and separates vector/raster link tests. |
| geonode/layers/models.py | Makes Dataset.store nullable/blank for remote datasets. |
| geonode/layers/migrations/0047_alter_dataset_store.py | Migration to make Dataset.store nullable. |
| geonode/layers/api/tests.py | Updates API listing/count expectations to exclude metadata-only datasets. |
| geonode/harvesting/tests/test_harvester_worker_wms.py | Adds test asserting harvested WMS resources have no store. |
| geonode/harvesting/migrations/0051_flag_harvested_resources_as_remote.py | Backfills harvested resources to sourcetype=REMOTE and sets remote_service links. |
| geonode/harvesting/harvesters/wms.py | Ensures harvested WMS datasets set subtype explicitly and store=None. |
| geonode/harvesting/harvesters/geonodeharvester.py | Ensures remote dataset descriptors/additional parameters use store=None. |
| geonode/harvesting/harvesters/base.py | Sets harvested resource sourcetype=REMOTE consistently (not conditional on bbox presence). |
| geonode/harvesting/harvesters/arcgis.py | Ensures harvested ArcGIS datasets set subtype and store=None. |
| geonode/geoserver/signals.py | Makes MapLayer→Dataset resolution tolerant of ambiguity when endpoints are shared across services. |
| geonode/geoserver/security.py | Skips GeoFence rule syncing for remote datasets and clears dirty state instead. |
| geonode/geoserver/manager.py | Uses is_remote_resource() for GeoServer existence checks rather than subtype heuristics. |
| geonode/base/api/tests.py | Updates base resource totals to match fixture/state changes. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| dataset_queryset = Dataset.objects.filter(Q(alternate=instance.name) | Q(name=instance.name)) | ||
| if instance.local and instance.store: | ||
| if instance.store: | ||
| dataset_queryset = dataset_queryset.filter(store=instance.store) | ||
| elif instance.ows_url: | ||
| dataset_queryset = dataset_queryset.filter(remote_service__base_url=instance.ows_url) |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 27 changed files in this pull request and generated no new comments.
Suppressed comments (2)
geonode/services/serviceprocessors/arcgis.py:142
- In ArcGIS service registration, the newly created Harvester is assigned to
new_instance.harvester, but the Service instance is not saved afterward. SinceService.harvesteris a DB field (FK), this assignment won't persist and the Service may end up without an attached harvester in the database.
new_instance.harvester = new_harvester
return new_instance
geonode/harvesting/migrations/0051_flag_harvested_resources_as_remote.py:3
- This data migration imports
SOURCE_TYPE_REMOTEfrom application code. Migrations should be self-contained so they keep working even ifgeonode.base.enumerationschanges in the future; prefer inlining the current literal value instead.
from django.db import migrations
from geonode.base.enumerations import SOURCE_TYPE_REMOTE
|
Superseded by #14536 |
This PR is built upon #14404 since it needs its fixes.
Checklist
For all pull requests:
The following are required only for core and extension modules (they are welcomed, but not required, for contrib modules):
Submitting the PR does not require you to check all items, but by the time it gets merged, they should be either satisfied or inapplicable.