Skip to content

Fix fleet output OAS regressions: SSL type explosion and Kafka union wrappers#260842

Merged
TinaHeiligers merged 13 commits intoelastic:mainfrom
TinaHeiligers:fix/oas-fleet-output-regressions
Apr 14, 2026
Merged

Fix fleet output OAS regressions: SSL type explosion and Kafka union wrappers#260842
TinaHeiligers merged 13 commits intoelastic:mainfrom
TinaHeiligers:fix/oas-fleet-output-regressions

Conversation

@TinaHeiligers
Copy link
Copy Markdown
Contributor

@TinaHeiligers TinaHeiligers commented Apr 2, 2026

Summary

Follow up to #258986 to fix duplicated SSL/shipper struct definitions and anyOf catchall union wrappers for Kafka conditional fields.

This PR:

  • extracts shared SSL and shipper objects into standalone schemas with meta:{id}
  • output variants reference a single $ref component
  • replaces the four schema.conditional() Kafka fields (compression_level, connection_type, username, password) with schema.maybe()
  • produces clean OAS output
  • moves cross-field validation to the service layer in output.ts, matching the existing pattern for compression_level clearing
  • allowlists breaking changes in OAS api contracts check

The net result is 27 fewer generated Go types and ~1,500 fewer lines in the bundled OAS. On the provider side, this eliminates the ~450 lines of adapter code added in PR#2031.

Relates to #228077

Breaking changes

The contract checker flags 352 changes across 3 fleet output endpoints. None of these change runtime API behavior — they're all OAS representation improvements that only affect code generators consuming the spec.

  • SSL/shipper object$ref: oasdiff can't resolve through $ref, so it reports type changes and "removed" sub-properties. The actual data shapes are identical.
  • Kafka fields became optional: schema.conditional()schema.maybe() is strictly more permissive. Service layer now enforces the same cross-field constraints.
  • Kafka field types narrowed: the old schema.conditional() produced anyOf: [array, boolean, number, object, string] catchalls. Now they're the correct specific types (number, string, etc.).
How to test this
  1. Run the output model and service tests:

    node scripts/jest x-pack/platform/plugins/shared/fleet/server/types/models/output.test.ts
    node scripts/jest x-pack/platform/plugins/shared/fleet/server/services/output.test.ts
    

    All 7 model tests and 99 service tests should pass.

  2. Run the contract checker for both distributions:

    node scripts/check_api_contracts.js --distribution stack
    node scripts/check_api_contracts.js --distribution serverless
    

    Both should report all breaking changes allowlisted.

  3. Verify OAS snapshot changes by inspecting the diff on oas_docs/output/kibana.yaml:

    • output_ssl and output_shipper each appear once as component definitions, referenced via $ref from all output variants
    • Kafka compression_level is type: number, connection_type is type: string with enum, username and password are nullable: true, type: string — no anyOf catchalls

Checklist

  • Unit or functional tests were updated or added to match the most common scenarios
  • This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The release_note:breaking label should be applied in these situations.
  • The PR description includes the appropriate Release Notes section, and the correct release_note:* label is applied per the guidelines

Identify risks

Risk Severity Likelihood Mitigation
Loosened Kafka field validation at schema level Low Low Service layer enforces the same cross-field rules. Existing service tests (99/99) pass.
Shared SSL/shipper $ref changes OAS structure Low None observed Data shape is identical. TF provider codegen produces cleaner types with no regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

@TinaHeiligers TinaHeiligers added release_note:skip Skip the PR/issue when compiling release notes backport:skip This PR does not require backporting Feature:OAS Work or issues related to Core-provided mechanisms for generating OAS Team:Fleet Team label for Observability Data Collection Fleet team labels Apr 2, 2026
Comment thread x-pack/platform/plugins/shared/fleet/server/services/output.ts
"path": "/api/fleet/outputs",
"method": "get",
"reason": "Extract shared OutputSslSchema/OutputShipperSchema with meta:{id} to fix type explosion, replace schema.conditional() Kafka fields with schema.maybe() to fix union wrapper regression. Coordinated with provider team — these changes eliminate ~450 lines of adapter code.",
"approvedBy": "@elastic/terraform-provider"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tobio these changes are needed to fix the regression we introduced.
They should clean up the OAS a bit

@TinaHeiligers TinaHeiligers marked this pull request as ready for review April 2, 2026 02:20
@TinaHeiligers TinaHeiligers requested review from a team as code owners April 2, 2026 02:20
@elasticmachine
Copy link
Copy Markdown
Contributor

Pinging @elastic/fleet (Team:Fleet)

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Apr 2, 2026

Approvability

Verdict: Needs human review

This PR introduces runtime behavior changes in fleet output service (clearing Kafka auth fields based on auth_type) in addition to OAS schema refactoring. The author does not own any of the modified files, and an open review comment asks whether the changes were tested with the provider code generator. The designated code owners (@elastic/fleet, @elastic/kibana-core) should review.

No code changes detected at 30b9dbf. Prior analysis still applies.

You can customize Macroscope's approvability policy. Learn more.

Copy link
Copy Markdown
Member

@tobio tobio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look simpler, curious what the enum:null translates to though.

Comment thread oas_docs/output/kibana.yaml Outdated
- certificate
- strict
type: string
anyOf:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we tested this structure with the provider code generator?

Copy link
Copy Markdown
Contributor Author

@TinaHeiligers TinaHeiligers Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested it — oapi-codegen breaks on the anyOf: [enum: [null], $ref: ...] pattern. Fixed the bundler in 085fd80 to emit nullable: true with an allOf wrapper instead, which is the canonical OAS 3.0.x representation and what oapi-codegen expects.

Copy link
Copy Markdown
Contributor Author

@TinaHeiligers TinaHeiligers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enum: [null] inside anyOf is the OAS 3.0 idiom for nullable — it's equivalent to nullable: true but works correctly with anyOf/oneOf compositions. In the generated TypeScript types it produces Type | null (e.g., ssl?: OutputSsl | null). For Go codegen it typically translates to a pointer type, which is the standard nullable representation.

Copy link
Copy Markdown
Contributor Author

@TinaHeiligers TinaHeiligers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tobio Correction on my earlier reply — I called this "the OAS 3.0 idiom for nullable" which isn't quite right. The canonical OAS 3.0.x pattern is nullable: true, what the schema was before.
anyOf: [enum: [null], $ref: ...] is technically valid but more aligned with OAS 3.1 semantics.

The pattern's not hand-authored, it's generated by the OAS bundler where the output's reflecting the fleet source schema schema.oneOf([schema.literal(null), ObjectSchema]).

Internally, the bundler serializes schema.literal(null) as enum: [null] rather than collapsing it to nullable: true.

For codegen consumers like the TF provider, most generators handle both patterns correctly since anyOf: [enum: [null], $ref] is structurally unambiguous. But it's worth flagging that we've moved away from the canonical 3.0.3 idiom in these specific schemas. If this causes issues downstream, the fix would be in the bundler's serialization logic rather than in the source schemas.

Copy link
Copy Markdown
Contributor Author

@TinaHeiligers TinaHeiligers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call asking about the codegen, @tobio. I ran oapi-codegen against the PR's bundled spec and it fails — the anyOf: [enum: [null], $ref: ...] pattern on shipper and ssl fields causes an unmarshal error.

The old spec used nullable: true (canonical OAS 3.0.3) which oapi-codegen handles fine. The issue's in Kibana's OAS bundler — replaceNullableOutputWithNullEnum in kbn-router-to-openapispec converts the internal nullable placeholder to enum: [null] instead of collapsing it to nullable: true. This affects shipper and ssl across all fleet output types, plus a searchAfter field.

Putting this back to draft while I work on a bundler fix.

@TinaHeiligers TinaHeiligers marked this pull request as draft April 2, 2026 23:00
Copy link
Copy Markdown
Contributor Author

@TinaHeiligers TinaHeiligers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds a fix to the bundler. processNullableOutput in enum.ts now handles $ref targets by wrapping them in allOf with nullable: true. Also replaced replaceNullableOutputWithNullEnum — it now strips the null placeholder branch and sets nullable: true on the parent instead of emitting enum: [null]. OAS snapshots regenerated, API contracts check passes, and oapi-codegen generates fleet types successfully. Ready for re-review.

@TinaHeiligers TinaHeiligers marked this pull request as ready for review April 3, 2026 02:04
schema.anyOf = remaining;
schema.nullable = true;
return true;
};
Copy link
Copy Markdown
Contributor Author

@TinaHeiligers TinaHeiligers Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elastic/kibana-core Two changes here, both fixing the same root issue: the bundler was emitting enum: [null] to represent nullable, which isn't canonical OAS 3.0 and breaks oapi-codegen (the Go code generator the Terraform provider uses).

processNullableOutput previously bailed when the non-null branch was a $ref, falling through to replaceNullableOutputWithNullEnum which produced enum: [null]. Now it wraps the $ref in allOf so nullable: true can sit as a sibling — OAS 3.0 doesn't allow siblings directly on a $ref.

replaceNullableOutputWithNullEnumreplaceNullableOutputWithNullable: the old function swapped the nullable placeholder for enum: [null] in multi-branch anyOf unions (3+ branches). The replacement strips the placeholder branch entirely and sets nullable: true on the parent schema. enum: [null] is an OAS 3.1 pattern where null is a first-class type — it doesn't belong in a 3.0.3 spec.

Copy link
Copy Markdown
Member

@florent-leborgne florent-leborgne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@TinaHeiligers
Copy link
Copy Markdown
Contributor Author

@elasticmachine merge upstream

@TinaHeiligers
Copy link
Copy Markdown
Contributor Author

@elasticmachine merge upstream

@criamico
Copy link
Copy Markdown
Member

criamico commented Apr 8, 2026

@TinaHeiligers Today I merged #259308 that touches some of the same files. If my changes don't look good enough feel free to address them with this PR instead.

…regressions

# Conflicts:
#	oas_docs/output/kibana.serverless.yaml
#	oas_docs/output/kibana.yaml
#	x-pack/platform/plugins/shared/fleet/server/types/models/output.ts
@TinaHeiligers
Copy link
Copy Markdown
Contributor Author

@criamico your changes are fine from an OAS quality perspective. The only thing it didn't include was to use an extracted OutputSslSchema ref for the ssl, which this PR does. I checked the provider codegen against this PR with conflicts resolved and there aren't any new issues.

@TinaHeiligers
Copy link
Copy Markdown
Contributor Author

@elasticmachine merge upstream

@TinaHeiligers
Copy link
Copy Markdown
Contributor Author

@elasticmachine merge upstream

@TinaHeiligers
Copy link
Copy Markdown
Contributor Author

@elasticmachine merge upstream

@elasticmachine
Copy link
Copy Markdown
Contributor

There are no new commits on the base branch.

@TinaHeiligers
Copy link
Copy Markdown
Contributor Author

@elasticmachine merge upstream

@elasticmachine
Copy link
Copy Markdown
Contributor

💛 Build succeeded, but was flaky

Failed CI Steps

Test Failures

  • [job] [logs] affected Scout: [ platform / security ] plugin / local-serverless-security_complete - [NON-MKI] UIAM API Keys grant and invalidate functions - should be able to grant a UIAM API key with valid UIAM credentials
  • [job] [logs] Fleet Cypress Tests #1 / View agents list Bulk actions should navigate to Maintenance and diagnostics submenu
  • [job] [logs] Fleet Cypress Tests #1 / View agents list Bulk actions should navigate to Security and removal submenu and show unenroll option
  • [job] [logs] Fleet Cypress Tests #1 / View agents list Bulk actions should show hierarchical menu with submenus

Metrics [docs]

✅ unchanged

History

Copy link
Copy Markdown
Contributor

@Bamieh Bamieh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@TinaHeiligers TinaHeiligers merged commit 3a1a338 into elastic:main Apr 14, 2026
17 checks passed
mbondyra added a commit to mbondyra/kibana that referenced this pull request Apr 14, 2026
* commit '11ed3645c5ededae2a6e29f2a79b31f52208b441': (157 commits)
  remove sync register uiAction methods (elastic#254590)
  [performance] Apply minimal auth to the search route (elastic#257497)
  [ES|QL] Reports correctly the controls server side errors (elastic#263020)
  [SecuritySolution][Navigation] Enable classic nav updates (elastic#262358)
  [Inference] Use pretty name and logo on feature settings page (elastic#262531)
  [Security Solution] fix AT-AB cypress test (elastic#262991)
  [SigEvents] Seed sigevents env script (elastic#261172)
  Adjust conditions for validating no refetch for expanded row (elastic#262978)
  [Agent Builder] update copy for the announcement modal (elastic#263034)
  [Search] Hide index management links for users without privileges (elastic#262627)
  Simplify OAS schema for GET `/api/spaces/space` query params (elastic#260831)
  Fix fleet output OAS regressions: SSL type explosion and Kafka union wrappers (elastic#260842)
  [Dashboards in chat] fix agent confusing the axes in a horizontal chat (elastic#263064)
  [One Workflow] Add alert state checkbox UI for workflow connector (elastic#259770)
  [One Workflow] Deprecate legacy Cases step types in workflow authoring (elastic#262070)
  skip failing test suite (elastic#248090)
  fix flaky test: MonitorDetails filter apply button not enabled (elastic#260788)
  fix: propagate AbortSignal to executeAsReasoningAgent for task cancellation (elastic#262811)
  [Security Solution][Alert KPI] Fix white space bug in alert KPIs (elastic#260803)
  [Streams] Move helpers and format_size_unit to utils folder (elastic#262550)
  ...

# Conflicts:
#	x-pack/platform/plugins/shared/dashboard_agent/public/attachment_types/canvas_integration/dashboard_canvas_content.test.tsx
#	x-pack/platform/plugins/shared/dashboard_agent/public/attachment_types/canvas_integration/dashboard_canvas_content.tsx
#	x-pack/platform/plugins/shared/dashboard_agent/public/attachment_types/canvas_integration/use_register_canvas_action_buttons.ts
#	x-pack/platform/plugins/shared/dashboard_agent/public/attachment_types/index.test.tsx
#	x-pack/platform/plugins/shared/dashboard_agent/public/attachment_types/index.tsx
@TinaHeiligers TinaHeiligers deleted the fix/oas-fleet-output-regressions branch April 14, 2026 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting Feature:OAS Work or issues related to Core-provided mechanisms for generating OAS release_note:skip Skip the PR/issue when compiling release notes Team:Fleet Team label for Observability Data Collection Fleet team v9.5.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants