Conversation
Convert storage metadata configs before traversing password fields and apply encryption to version-checked writes and history snapshots. Keep application private config out of persistence and API responses, restoring it from the owning application when preparing runner payloads. Guard deployment and managed-secret writes with the pipeline row lock and version check, and skip deployment during history consolidation. Refs #32693
❌ PR checklist incompleteThis PR cannot be merged until the following are addressed on its linked issue:
The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically. Maintainers can bypass this check by adding the |
| @@ -1216,6 +1246,9 @@ public IngestionPipelineUpdater( | |||
| @Transaction | |||
There was a problem hiding this comment.
💡 Performance: Row lock held across external deploy HTTP call
lockPipelineForUpdate() takes a SELECT ... FOR UPDATE row lock (and pins the DB connection) at the start of the baseline pass, and the same @Transaction later runs deployIfRequired, which makes a synchronous HTTP call to Airflow/K8s. The lock and the pooled connection are therefore held for the full duration of the external deploy. Concurrent updates to the same pipeline serialize behind this lock, and a slow or hung pipeline service extends lock-hold time, risking lock-wait timeouts and connection-pool pressure. This is largely intentional per the PR description, but consider bounding the deploy call with an aggressive timeout so a stuck runner cannot pin the row lock indefinitely.
Was this helpful? React with 👍 / 👎
| private void lockPipelineForUpdate() { | ||
| // Keep deployment and managed-secret writes behind the same lock as the version check. | ||
| final IngestionPipeline stored = | ||
| dao.jsonToEntity(dao.findJsonByIdForUpdate(original.getId(), ALL), original.getId()); | ||
| if (isUseOptimisticLocking() && !Objects.equals(stored.getVersion(), original.getVersion())) { | ||
| throw new PreconditionFailedException( | ||
| "The entity has been modified by another user. Please refresh and retry."); | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Edge Case: Possible NPE in lockPipelineForUpdate on concurrent delete
dao.jsonToEntity(dao.findJsonByIdForUpdate(id, ALL), id) returns null when the row no longer exists (jsonToEntity returns null for null json). If the pipeline is deleted concurrently before the lock is acquired, stored.getVersion() throws a NullPointerException that surfaces as a 500 instead of a clean 404/409. Guard against a null stored and translate it into an appropriate not-found/conflict response.
Was this helpful? React with 👍 / 👎
Code Review
|
| Compact |
|
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source
|



Describe your changes:
Fixes #32693
Storage metadata manifest credentials in
sourceConfig.configbypassed password-field encryption and masking because the nested configuration remained an untyped map. Convert these configurations before traversing secrets so stored credentials are encrypted and API responses are masked. Keep application private configuration out of persisted pipelines and API responses, and restore it when building runner payloads.Type of change:
High-level design:
No schema or API signature changes. Existing plaintext records are encrypted on their next write; legacy history is masked on read. No bulk data migration is included.
Tests:
Use cases covered
Unit tests
226 focused Java tests passed, with no failures or skips. Added/updated:
IngestionPipelineSecretsTest,IngestionPipelinePersistenceSecretsTest,IngestionPipelineStorageStrippingTest,IngestionPipelineHistorySecretsTest,ApplicationWorkflowConfigTest, andAirflowRESTClientTest. The run also included existing secrets, converter, Airflow, and Kubernetes tests.Combined JaCoCo evidence from focused tests and instrumented API execution covers 130/130 changed executable lines.
ApplicationWorkflowConfig,StorageServiceMetadataPipelineClassConverter,ClassConverterFactory, andIngestionPipelineBuilderhave 100% line coverage.The 90% whole-class coverage guideline is not met for six existing files in this focused run:
AirflowRESTClient85.6%,EntityRepository31.8%,IngestionPipelineRepository34.4%,IngestionPipelineResource16%,SecretsManager44.1%, andPasswordEntityMasker81.4%.Backend integration tests
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineSecretsIT.java: 4 cases passed against real MySQL, with controlled runner and secrets-manager boundaries. Covers consolidation, matching display names, and concurrent accepted/rejected saves for deployed and undeployed pipelines.Ingestion integration tests
Not applicable: no Python ingestion changes. Actual scheduler execution and cloud authentication were not validated.
Playwright (UI) tests
No UI source changes or committed Playwright specifications. Local Chromium validation exercised the real UI, API, and MySQL with a controlled deployment boundary.
Manual testing performed
Validation limits and guideline findings
final, and some test methods exceed the approximate method-length guideline. Spotless passes and does not enforce these rules.git diff --checkpassed.UI screen recording / screenshots:
Not applicable: no UI source changes.
Checklist:
Fixes <issue-number>: <short explanation>.Fixes #32693.