Skip to content

Commit

Permalink
UPSTREAM: 91295: kubectl explain: detect resource group in case there…
Browse files Browse the repository at this point in the history
… are two or more groups discovered

Assume the following CRDs exist (ordered by priority, the first is the highest):
- authentications.migration.k8s.io (K=Authentications, G=migration.k8s.io)
- authentications.metal3.io (K=Authentications, G=metal3.io)
- authentications.whereabouts.cni.cncf.io (K=Authentications, G=whereabouts.cni.cncf.io)
- authentications.snapshot.storage.k8s.io (K=Authentications, G=snapshot.storage.k8s.io)

In case 'kubectl explain authentications' is ran, the highest priority definition (in this case authentications.migration.k8s.io)
is returned. In case a user wants to explain authentication CRD of a different group, --api-version flag has to be set alongside
to point to a specific group and version. E.g. --api-version=metal3.io/v1

This PR allows to dismiss --api-version flag and perform a prefix check to select a resource (e.g. CRD) whose (resource, group) pair
fully prefixes requested resource. E.g. running 'kubectl explain authentications.metal3.io' will return
description of authentications.metal3.io. The same holds for optional field path.
I.e. 'kubectl explain authentications.metal3.io.spec' will return description of spec field
of authentications.metal3.io.spec. In case no resource match is found, the search falls back
to selecting the highest priority gvr that matches the resource.
In case --api-version is set, no prefix matching is performed. To cover cases
such as 'kubectl explain authentications.metal3.io --api-version=authentications.metal3.io/v1' where
fields path coincide with the resource fully specified name (to access .metal3.io field of authentications.metal3.io).
  • Loading branch information
ingvagabund committed Mar 8, 2021
1 parent aa519d9 commit 81af0d9
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 6 deletions.
22 changes: 16 additions & 6 deletions staging/src/k8s.io/kubectl/pkg/cmd/explain/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,22 @@ func (o *ExplainOptions) Run(args []string) error {
recursive := o.Recursive
apiVersionString := o.APIVersion

// TODO: After we figured out the new syntax to separate group and resource, allow
// the users to use it in explain (kubectl explain <group><syntax><resource>).
// Refer to issue #16039 for why we do this. Refer to PR #15808 that used "/" syntax.
fullySpecifiedGVR, fieldsPath, err := explain.SplitAndParseResourceRequest(args[0], o.Mapper)
if err != nil {
return err
var fullySpecifiedGVR schema.GroupVersionResource
var fieldsPath []string
var err error
if len(apiVersionString) == 0 {
fullySpecifiedGVR, fieldsPath, err = explain.SplitAndParseResourceRequestWithMatchingPrefix(args[0], o.Mapper)
if err != nil {
return err
}
} else {
// TODO: After we figured out the new syntax to separate group and resource, allow
// the users to use it in explain (kubectl explain <group><syntax><resource>).
// Refer to issue #16039 for why we do this. Refer to PR #15808 that used "/" syntax.
fullySpecifiedGVR, fieldsPath, err = explain.SplitAndParseResourceRequest(args[0], o.Mapper)
if err != nil {
return err
}
}

gvk, _ := o.Mapper.KindFor(fullySpecifiedGVR)
Expand Down
37 changes: 37 additions & 0 deletions staging/src/k8s.io/kubectl/pkg/explain/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,43 @@ func SplitAndParseResourceRequest(inResource string, mapper meta.RESTMapper) (sc
if err != nil {
return schema.GroupVersionResource{}, nil, err
}

return gvr, fieldsPath, nil
}

// SplitAndParseResourceRequestWithMatchingPrefix separates the users input into a model and fields
// while selecting gvr whose (resource, group) prefix matches the resource
func SplitAndParseResourceRequestWithMatchingPrefix(inResource string, mapper meta.RESTMapper) (gvr schema.GroupVersionResource, fieldsPath []string, err error) {
// ignore trailing period
inResource = strings.TrimSuffix(inResource, ".")
dotParts := strings.Split(inResource, ".")

gvrs, err := mapper.ResourcesFor(schema.GroupVersionResource{Resource: dotParts[0]})
if err != nil {
return schema.GroupVersionResource{}, nil, err
}

for _, gvrItem := range gvrs {
// Find first gvr whose gr prefixes requested resource
groupResource := gvrItem.GroupResource().String()
if strings.HasPrefix(inResource, groupResource) {
resourceSuffix := inResource[len(groupResource):]
if len(resourceSuffix) > 0 {
dotParts := strings.Split(resourceSuffix, ".")
if len(dotParts) > 0 {
fieldsPath = dotParts[1:]
}
}
return gvrItem, fieldsPath, nil
}
}

// If no match, take the first (the highest priority) gvr
if len(gvrs) > 0 {
gvr = gvrs[0]
_, fieldsPath = splitDotNotation(inResource)
}

return gvr, fieldsPath, nil
}

Expand Down

0 comments on commit 81af0d9

Please sign in to comment.