Skip to content

POC: Preserve the request scope's Kind through response object encoding - #141344

Draft
benluddy wants to merge 2 commits into
kubernetes:masterfrom
benluddy:multi-kind-scheme
Draft

benluddy wants to merge 2 commits into
kubernetes:masterfrom
benluddy:multi-kind-scheme

Conversation

@benluddy

Copy link
Copy Markdown
Contributor

What type of PR is this?

What this PR does / why we need it:

Which issue(s) this PR is related to:

Special notes for your reviewer:

Does this PR introduce a user-facing change?


Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:


@kubernetes-prow kubernetes-prow Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 12, 2026
@kubernetes-prow

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@kubernetes-prow

Copy link
Copy Markdown
Contributor

Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our release note process to remove it.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@kubernetes-prow kubernetes-prow Bot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. do-not-merge/needs-kind Indicates a PR lacks a `kind/foo` label and requires one. do-not-merge/needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Aug 12, 2026
@kubernetes-prow

Copy link
Copy Markdown
Contributor

Please note that we're already in Code Freeze for the upcoming v1.37.0 release.

Adding the milestone to this PR is strictly prohibited without proper approval. If this PR needs to be included in the v1.37.0 release:

  1. Technical review: get the PR reviewed and approved as usual (/lgtm and /approve)
  2. Inclusion in release: tag @kubernetes/sig-release-leads on this PR and ping the #sig-release Slack channel to request adding the v1.37.0 milestone

We're also in Test Freeze for the release-1.37 branch. This means every merged PR will be automatically fast-forwarded via the periodic ci-fast-forward job to the release branch of the upcoming v1.37.0 release.

Fast forwards are scheduled to happen every 6 hours, whereas the most recent run was: Wed Aug 12 18:39:00 UTC 2026.

@kubernetes-prow

Copy link
Copy Markdown
Contributor

This issue is currently awaiting triage.

If a SIG or subproject determines this is a relevant issue, they will accept it by applying the triage/accepted label and provide further guidance.

The triage/accepted label can be added by org members by writing /triage accepted in a comment.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@kubernetes-prow kubernetes-prow Bot added needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. labels Aug 12, 2026

// LogResponseObject fills in the response object into an audit event. The passed runtime.Object
// will be converted to the given gv.
func LogResponseObject(ctx context.Context, obj runtime.Object, gv schema.GroupVersion, s runtime.NegotiatedSerializer) {

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.

There are a few signatures like this one that can be widened to accept the interface runtime.GroupVersioner instead of schema.GroupVersion. These parameters were ultimately used as an Encoder's GroupVersioner anyway.

Kind schema.GroupVersionKind
Resource schema.GroupVersionResource
Kind schema.GroupVersionKind
ResourceVersioner ResourceGVK

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.

Or a sibling ListKind?

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.

what does Kind contain for list operations? Pod or PodList?

// the caller may return a flag indicating whether the result should be flushed as writes occur
// and a content type string that indicates the type of the stream.
// If a null stream is returned, a StatusNoContent response wil be generated.
InputStream(ctx context.Context, apiVersion, acceptHeader string) (stream io.ReadCloser, flush bool, mimeType string, err error)

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.

I didn't find any real implementations using this parameter, so removing it was the simplest option. I don't think this approach requires breaking this interface, though.

Comment on lines +67 to +76
var _ rest.GroupVersionKindProvider = &multiKindStorage{}
var _ rest.GroupVersionListKindProvider = &multiKindStorage{}

func (s *multiKindStorage) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
return containingGV.WithKind(s.kind)
}

func (s *multiKindStorage) GroupVersionListKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
return containingGV.WithKind(s.kind + "List")
}

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.

Without these, today:

F0806 13:38:16.417302  805429 openapi.go:46] Failed to build open api spec for root: duplicate Operation ID listTestgroupV1NamespacedFoo for path
  /apis/testgroup/v1/namespaces/{namespace}/bars and /apis/testgroup/v1/namespaces/{namespace}/foos

and

{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "testgroup/v1",
  "resources": [
    {
      "name": "bars",
      "singularName": "bar",
      "namespaced": true,
      "kind": "Foo",  <--------------
      "verbs": [
        "get",
        "list"
      ]
    },
    {
      "name": "foos",
      "singularName": "foo",
      "namespaced": true,
      "kind": "Foo",
      "verbs": [
        "get",
        "list"
      ]
    }
  ]
}

@kubernetes-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: benluddy
Once this PR has been reviewed and has the lgtm label, please assign sttts for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubernetes-prow kubernetes-prow Bot added sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. sig/auth Categorizes an issue or PR as relevant to SIG Auth. labels Aug 12, 2026
@kubernetes-prow kubernetes-prow Bot removed the do-not-merge/needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. label Aug 12, 2026
}
if isLister {
if listKindProvider, ok := storage.(rest.GroupVersionListKindProvider); ok {
reqScope.ResourceVersioner.ListKind = listKindProvider.GroupVersionListKind(a.group.GroupVersion)

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.

I don't think it would be safe to assume the "singular kind + 'List'" convention for all API servers.

@@ -395,7 +395,6 @@ func (storage *SimpleRESTStorage) List(ctx context.Context, options *metainterna
}

type SimpleStream struct {

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.

nit: add a ResourceStreamer type assertion on this bit to directly catch the signature change?

out, flush, contentType, err := stream.InputStream(req.Context(), req.Header.Get("Accept"))
if err != nil {
ErrorNegotiated(err, s, gv, w, req)
ErrorNegotiated(err, s, schema.GroupVersion{}, w, req)

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.

passing schema.GroupVersion{} for GV here looks iffy

if callers like WriteObjectNegotiated have the groupversion today, switching the signature to GroupVersioner would make sense rather than throwing away that info

eitherwise, using either metav1.Unversioned or populating a groupversion out of the RequestInfo APIGroup/APIVersion pulled from the context might make more sense

case (target.Kind == "PartialObjectMetadata" || target.Kind == "PartialObjectMetadataList" || target.Kind == "Table") &&
(target.GroupVersion() == metav1beta1.SchemeGroupVersion || target.GroupVersion() == metav1.SchemeGroupVersion):
return *target, apihelpers.GetMetaInternalVersionCodecs(), true
return target.GroupVersion(), apihelpers.GetMetaInternalVersionCodecs(), true

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.

why throw away the kind info here? I thought we intended to make GVK implement GroupVersioner


func (r ResourceGVK) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
for _, k := range kinds {
if k == r.Kind || (!r.ListKind.Empty() && k == r.ListKind) {

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.

nit, hoist !r.ListKind.Empty() outside the loop

}
for _, k := range kinds {
if k.Group == r.Kind.Group && k.Version == r.Kind.Version {
return k, true

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.

returning a kind other than r.Kind.Kind is odd, right? what happens if we don't have this fallback? same question below for the group-only match

// type is registered under multiple Kind names in the same GroupVersion.
type ResourceGVK struct {
Kind schema.GroupVersionKind
ListKind schema.GroupVersionKind

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.

storing a fully-qualified ListKind makes it possible for ListKind to not match the group or version of Kind (which would be weird) ... think about how to make that mismatch impossible

Kind schema.GroupVersionKind
Resource schema.GroupVersionResource
Kind schema.GroupVersionKind
ResourceVersioner ResourceGVK

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.

what does Kind contain for list operations? Pod or PodList?

@kubernetes-prow kubernetes-prow Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 25, 2026
@kubernetes-prow

Copy link
Copy Markdown
Contributor

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

This branch has not been deployed

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

Labels

area/apiserver cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. do-not-merge/needs-kind Indicates a PR lacks a `kind/foo` label and requires one. do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. sig/auth Categorizes an issue or PR as relevant to SIG Auth. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

Status: Needs Triage

Development

Successfully merging this pull request may close these issues.

2 participants