Skip to content

[CRD] Mark ray.io/v1alpha1 as a deprecated API version - #5122

Open
dstrodtman wants to merge 1 commit into
masterfrom
djs-260810-deprecate-ray-v1alpha1
Open

[CRD] Mark ray.io/v1alpha1 as a deprecated API version#5122
dstrodtman wants to merge 1 commit into
masterfrom
djs-260810-deprecate-ray-v1alpha1

Conversation

@dstrodtman

@dstrodtman dstrodtman commented Aug 10, 2026

Copy link
Copy Markdown

Why are these changes needed?

ray.io/v1alpha1 is served but not stored, and has been since #1482 made v1 the storage version in October 2023. Today nothing tells users that. There's no deprecated flag on the version, no deprecationWarning, and no conversion webhook, so kubectl apply of a ray.io/v1alpha1 manifest succeeds with no output at all.

That silence is the problem, because the two versions are far apart now:

  • ray.io/v1 exposes 63 exported types; ray.io/v1alpha1 exposes 27.
  • Everything added since December 2023 is v1-only: authOptions, tlsOptions, networkPolicy, gcsFaultToleranceOptions, historyServerOptions, deletionPolicy, the RayCluster and RayService upgrade strategies, and RayCronJob.
  • RayClusterSpec has 14 fields in v1 and 7 in v1alpha1.

None of the CRDs declare a conversion: stanza, so the strategy is None and fields absent from the v1alpha1 schema are pruned on that request path. A user who applies a v1alpha1 RayCluster with tlsOptions set gets a cluster without TLS and no error.

Worth flagging that this isn't just an unfortunate side effect. The CRD versioning docs describe strategy: None as being for the case where all served versions share an identical schema, since the only thing the API server rewrites is the apiVersion field. Ours diverged a long time ago — 14 fields vs. 7 on RayClusterSpec alone. Serving two materially different schemas through a no-op converter is what produces the silent pruning, so the behavior is the predictable consequence of the current configuration rather than a bug in it.

The v1alpha1 Go types also aren't wired into anything anymore. main.go registers only rayv1 in the manager scheme, the generated clientset under pkg/client/.../typed/ray/ contains only v1, and no non-generated file in the repo imports apis/ray/v1alpha1. The package exists so controller-gen still emits the schema. Its last functional change was #1788 on 2023-12-29.

This PR adds the missing signal. It doesn't remove anything.

What changed

Added +kubebuilder:deprecatedversion:warning=... to the three v1alpha1 root types, plus a Go Deprecated: doc comment on each, then regenerated.

The generated CRD change is three stanzas, one per kind:

  - deprecated: true
    deprecationWarning: ray.io/v1alpha1 RayJob is deprecated; use ray.io/v1 RayJob.
      v1alpha1 receives no new fields and will be removed in a future release.
    name: v1alpha1

No schema churn beyond that.

The Deprecated: doc comments do double duty. They don't reach the CRD, because CRD_OPTIONS sets maxDescLen=0, but they do reach docs/reference/api.md, and they give Go consumers a staticcheck SA1019 warning when they import the v1alpha1 types.

Files:

File Change
ray-operator/apis/ray/v1alpha1/{raycluster,rayjob,rayservice}_types.go Hand-edited: marker + Deprecated: comment
ray-operator/config/crd/bases/ray.io_ray{clusters,jobs,services}.yaml Generated by make manifests
helm-chart/kuberay-operator/crds/ray.io_ray{clusters,jobs,services}.yaml Generated by make helm
docs/reference/api.md Generated by make api-docs

This is the deprecation step of a sequence that stalled

Framing this as "a new deprecation" undersells it. The standard removal runway for a CRD version is four steps, stated most explicitly in Gateway API's versioning policy:

Within the Standard Channel, the removal of an API version is spread into at least four minor releases: a newer API version is configured as the storage version, the version is deprecated, the version is no longer served but still included in the CRD, and finally the version is no longer included in the CRD.

Where KubeRay actually sits:

Step Status
1. Newer version becomes the storage version Done — #1482, October 2023
2. Old version marked deprecated This PR
3. served: false, still present in the CRD Open
4. Removed from the CRD Open

Step 1 landed almost three years ago and step 2 was never taken. That's the whole reason there's no user-visible signal today, and it's why I'd frame this as finishing something rather than starting it.

Should v1alpha1 be removed instead?

I think yes, eventually, and I'd like maintainer input on the timing. I'm not proposing removal in this PR because step 3 and step 4 need a deprecation period that has never formally started — but I don't think "keep it indefinitely" is the alternative either.

The precedent says removal is normal. Kubernetes' own deprecation policy Rule #4a says alpha versions "may be removed in any release without prior deprecation notice." That policy governs core APIs rather than CRDs, so it isn't binding here, but it's the rung v1alpha1 sits on. Two comparable projects, both with larger blast radius than KubeRay:

Project Stable version introduced Old versions removed Window
cert-manager v1.0.0 (2020-09-02) v1.7.0 (2022-01-26) — dropped v1alpha2, v1alpha3, v1beta1 ~17 months
Cluster API v1.0.0 (2021-10-06) v1.6.0 dropped v1alpha3; v1.7.0 (2024-04-16) dropped v1alpha4 ~30 months
KubeRay v1.0.0 (2023-11-07) still served ~33 months

The spec argument is the strongest one. As noted above, strategy: None is specified for versions sharing one schema, and ours haven't for years. The two ways back into spec are a conversion webhook or removal. Writing and maintaining a converter for a version nobody develops against is hard to justify, which leaves removal.

But a straight removal PR still shouldn't go first, for two reasons.

First, downstream consumers. #1771 deleted v1alpha1 in December 2023 and #1784 reverted it four days later because Kueue couldn't straddle both versions; the plan recorded there was removal in 1.2.0, and we're on 1.6.2. Kueue itself is resolved — kubernetes-sigs/kueue now has 43 references to apis/ray/v1 and zero to v1alpha1. The class of problem isn't:

  • GitHub code search returns on the order of 1,500 YAML files matching ray.io/v1alpha1, including manifests in awslabs/ai-on-eks, Azure-Samples/aks-labs, data-prep-kit/data-prep-kit, and project-codeflare/multi-cluster-app-dispatcher. Some of those hits are config.ray.io/v1alpha1, a different and current API group, so treat that as an upper bound.
  • At least one live Go consumer still builds on the v1alpha1 types: koordinator-sh/koord-queue, last pushed 2026-04-27, whose pkg/jobext/handles/rayjob.go reconciles rayv1alpha1.RayJob. Same shape as the Kueue problem in 2023. The Deprecated: doc comments in this PR are what would give that project a staticcheck signal.

Second, status.storedVersions. Removal isn't purely a code deletion: the API server rejects removing a version from spec.versions while it remains in status.storedVersions, and that field doesn't self-clean. Any cluster that installed KubeRay before v1.0 still lists v1alpha1 there until every object is rewritten and the status is patched. cert-manager handled this by making it a hard user prerequisite in their 1.6 to 1.7 upgrade notes rather than shipping automation. RayClusters are much shorter-lived than Certificates so most clusters will already be clean, but the eventual removal PR needs an upgrade note with a verification command, not just the deletion.

KubeRay's own published policy suggests the cadence. The API reference page in Ray docs already carries a "KubeRay API compatibility and guarantees" section, which says maintainers "preserve the right to mark fields as deprecated and remove functionality associated with deprecated fields after a minimum of two minor releases."

That clause is scoped to fields within v1, and the section is silent on non-v1 API versions, so it doesn't settle this on its own. But two minor releases is the project's own stated interval for deprecate-then-remove, and applying it here gives a defensible timeline without importing anyone else's: deprecate in the next release, then served: false and removal from 1.9 onward. That's a stronger basis than the Gateway API runway I quoted above, which I'd treat as the sequence of steps rather than the schedule.

So my suggestion is: land the warning now, take step 3 once it's been out for the interval above, then step 4. Happy to write either. If you'd rather go straight to removal, or name a target release now, say so and I'll follow up with that PR instead.

The one documentation gap worth naming: that compatibility section covers field stability inside v1 and says nothing about the lifecycle of API versions. Extending it to cover version deprecation and removal would have prevented this drift, but it lives in the Ray repo rather than here, so it's a separate change with a different reviewer. I'm happy to draft it if maintainers want it.

Related issue number

None. Happy to file one if you'd prefer this tracked as an issue first.

Adjacent but separate: #5091 covers the anchor collision between the v1 and v1alpha1 sections of docs/reference/api.md. Removing v1alpha1 from generation would resolve it, which is part of why the removal question matters, but this PR doesn't touch that.

Labels

  • If this PR has user-facing changes that require documentation updates at release time, I have added the doc-updates-required label.
  • If this PR contains breaking changes, I have added the breaking-change label.

Not breaking: this adds a warning and removes nothing. v1alpha1 remains served and every existing manifest still applies.

Checks

  • I've made sure the tests are passing.
  • Testing Strategy
    • Unit tests
    • Manual tests
    • This PR is not tested :(

Manual test instructions

Confirm the generated output and that the API server surfaces the warning.

  1. Verify the generators are deterministic and the committed files match:
cd ray-operator
make manifests helm api-docs
git diff --exit-code   # expect no output
./hack/verify-api-docs.sh
  1. Confirm the version flags are right — v1 stored, v1alpha1 deprecated:
for f in config/crd/bases/ray.io_ray{clusters,jobs,services}.yaml; do
  echo "## $f"
  grep -nE "^  - name: v1|^    name: v1|^    served:|^    storage:|^    deprecated:" "$f"
done
  1. Check the warning reaches a client. Against a cluster with the new CRDs applied:
kubectl apply -f - <<'EOF'
apiVersion: ray.io/v1alpha1
kind: RayCluster
metadata:
  name: deprecation-warning-check
spec:
  rayVersion: 2.9.0
  headGroupSpec:
    rayStartParams: {}
    template:
      spec:
        containers:
        - name: ray-head
          image: rayproject/ray:2.9.0
EOF

Expected: Warning: ray.io/v1alpha1 RayCluster is deprecated; use ray.io/v1 RayCluster. v1alpha1 receives no new fields and will be removed in a future release. The object is still created.

The same manifest with apiVersion: ray.io/v1 produces no warning.

Verified locally: make manifests, make helm, and make api-docs all produce exactly the diff in this PR, and go build ./... and go vet ./apis/ray/v1alpha1/ are clean with controller-gen@v0.16.5.

Add `+kubebuilder:deprecatedversion:warning` to the v1alpha1 RayCluster,
RayJob, and RayService root types so the API server emits `deprecated: true`
and a `deprecationWarning` for that version. `kubectl` now warns on
`ray.io/v1alpha1` manifests instead of accepting them silently.

v1alpha1 has been served but not stored since #1482 set v1 as the storage
version in October 2023. Its Go types last changed functionally in #1788
(December 2023), and nothing in the operator imports them: the manager scheme
registers only rayv1 and the generated clientset contains only v1. Users on
v1alpha1 therefore get a schema frozen at its December 2023 field set, with
newer fields pruned on that request path, and no signal that this is
happening.

This change is not breaking. It adds a warning and removes nothing.

Also add a `Deprecated:` doc comment to each type so Go consumers get a
staticcheck SA1019 signal, and regenerate the CRDs, the Helm chart copies,
and docs/reference/api.md.

Signed-off-by: Douglas Strodtman <douglas@anyscale.com>
@dstrodtman dstrodtman added the doc-updates-required PR introduces user-facing changes that require documentation updates before the next release. label Aug 10, 2026
@dstrodtman
dstrodtman marked this pull request as ready for review August 10, 2026 18:49
@dstrodtman

Copy link
Copy Markdown
Author

cc @kevin85421 @andrewsykim for review.

@alculquicondor @tenzen-y — tagging you because you were on the #1784 thread that reverted the v1alpha1 removal in December 2023. Nothing here removes anything; this only adds the deprecation warning that step never got. But since that revert was made on Kueue's behalf, you should know the state has changed: kubernetes-sigs/kueue now references apis/ray/v1 in 43 places and v1alpha1 in none, so the original blocker is resolved on your side. If you have a view on the removal timing discussed in the description, it'd be welcome.

One heads-up for anyone looking at CI: the Build historyserver failure on this PR is pre-existing on master, not from this change. It's undefined: utils.ClusterKey in historyserver/pkg/historyserver/byte_cache_integration_test.go, introduced by #4960 and already being fixed in #5116. The checks that actually gate this change — ray-operator-verify-api-docs, ray-operator-verify-crd-rbac, and helm-chart-verify-crd — are green.

@tenzen-y

Copy link
Copy Markdown
Contributor

cc @kevin85421 @andrewsykim for review.

@alculquicondor @tenzen-y — tagging you because you were on the #1784 thread that reverted the v1alpha1 removal in December 2023. Nothing here removes anything; this only adds the deprecation warning that step never got. But since that revert was made on Kueue's behalf, you should know the state has changed: kubernetes-sigs/kueue now references apis/ray/v1 in 43 places and v1alpha1 in none, so the original blocker is resolved on your side. If you have a view on the removal timing discussed in the description, it'd be welcome.

One heads-up for anyone looking at CI: the Build historyserver failure on this PR is pre-existing on master, not from this change. It's undefined: utils.ClusterKey in historyserver/pkg/historyserver/byte_cache_integration_test.go, introduced by #4960 and already being fixed in #5116. The checks that actually gate this change — ray-operator-verify-api-docs, ray-operator-verify-crd-rbac, and helm-chart-verify-crd — are green.

Thank you for tagging me here. Yes, Kueue currently doesn't rely on Ray alpha API. I'm ok with removing alpha api anytime.

Please also check with other Kueue core team members
cc/ @mimowo @olekzabl @sohankunkerkar @amy @kannon92

@dstrodtman

Copy link
Copy Markdown
Author

Note I've also started a downstream cleanup in Ray docs: ray-project/ray#65336

@andrewsykim Appreciate your review there, and will follow up with other Ray maintainers about making sure all the tests are configured correctly.

@machichima machichima left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM! Thank you.
Created a issue to track the deprecation and remove progress: #5124

@machichima

Copy link
Copy Markdown
Collaborator

Could we also update

// +groupName=ray.io
package v1alpha1

Maybe change to

// Package v1alpha1 contains API Schema definitions for the ray v1alpha1 API group.
// +groupName=ray.io
//
// Deprecated: ray.io/v1alpha1 is frozen at its December 2023 feature set. Use
// github.com/ray-project/kuberay/ray-operator/apis/ray/v1 instead.
package v1alpha1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

doc-updates-required PR introduces user-facing changes that require documentation updates before the next release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants