diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index c3ecf273a67..cdf47c76b54 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -729,6 +729,13 @@ __EOF__ kubectl autoscale -f hack/testdata/frontend-controller.yaml --save-config "${kube_flags[@]}" --max=2 # Post-Condition: hpa "frontend" has configuration annotation [[ "$(kubectl get hpa.extensions frontend -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]] + # Ensure we can interact with HPA objects in lists through both the extensions/v1beta1 and autoscaling/v1 APIs + output_message=$(kubectl get hpa -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}") + kube::test::if_has_string "${output_message}" 'extensions/v1beta1' + output_message=$(kubectl get hpa.extensions -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}") + kube::test::if_has_string "${output_message}" 'extensions/v1beta1' + output_message=$(kubectl get hpa.autoscaling -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}") + kube::test::if_has_string "${output_message}" 'autoscaling/v1' # Clean up # Note that we should delete hpa first, otherwise it may fight with the rc reaper. kubectl delete hpa frontend "${kube_flags[@]}" diff --git a/pkg/kubectl/cmd/autoscale.go b/pkg/kubectl/cmd/autoscale.go index 53645184142..2e54c3c0e56 100644 --- a/pkg/kubectl/cmd/autoscale.go +++ b/pkg/kubectl/cmd/autoscale.go @@ -136,7 +136,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), Decoder: f.Decoder(true), } - hpa, err := resourceMapper.InfoForObject(object) + hpa, err := resourceMapper.InfoForObject(object, nil) if err != nil { return err } diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index 7236eb34c93..9ed779eecee 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -236,7 +236,7 @@ func RunCreateSubcommand(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, RESTMapper: mapper, ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), } - info, err := resourceMapper.InfoForObject(obj) + info, err := resourceMapper.InfoForObject(obj, nil) if err != nil { return err } diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index f4d397c54ab..af7793b2c80 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -212,7 +212,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), Decoder: f.Decoder(true), } - info, err = resourceMapper.InfoForObject(object) + info, err = resourceMapper.InfoForObject(object, nil) if err != nil { return err } diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index 2cf823c5992..1c28821e721 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -441,7 +441,7 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), Decoder: f.Decoder(true), } - info, err := resourceMapper.InfoForObject(obj) + info, err := resourceMapper.InfoForObject(obj, nil) if err != nil { return nil, "", nil, nil, err } diff --git a/pkg/kubectl/resource/mapper.go b/pkg/kubectl/resource/mapper.go index 0839ca4a8ea..25fd97d90bd 100644 --- a/pkg/kubectl/resource/mapper.go +++ b/pkg/kubectl/resource/mapper.go @@ -21,6 +21,7 @@ import ( "reflect" "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" ) @@ -85,11 +86,17 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) { // InfoForObject creates an Info object for the given Object. An error is returned // if the object cannot be introspected. Name and namespace will be set into Info // if the mapping's MetadataAccessor can retrieve them. -func (m *Mapper) InfoForObject(obj runtime.Object) (*Info, error) { - groupVersionKind, err := m.ObjectKind(obj) +func (m *Mapper) InfoForObject(obj runtime.Object, preferredGVKs []unversioned.GroupVersionKind) (*Info, error) { + groupVersionKinds, err := m.ObjectKinds(obj) if err != nil { return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err) } + + groupVersionKind := groupVersionKinds[0] + if len(groupVersionKinds) > 1 && len(preferredGVKs) > 0 { + groupVersionKind = preferredObjectKind(groupVersionKinds, preferredGVKs) + } + mapping, err := m.RESTMapping(groupVersionKind.GroupKind(), groupVersionKind.Version) if err != nil { return nil, fmt.Errorf("unable to recognize %v: %v", groupVersionKind, err) @@ -111,3 +118,39 @@ func (m *Mapper) InfoForObject(obj runtime.Object) (*Info, error) { ResourceVersion: resourceVersion, }, nil } + +// preferredObjectKind picks the possibility that most closely matches the priority list in this order: +// GroupVersionKind matches (exact match) +// GroupKind matches +// Group matches +func preferredObjectKind(possibilities []unversioned.GroupVersionKind, preferences []unversioned.GroupVersionKind) unversioned.GroupVersionKind { + // Exact match + for _, priority := range preferences { + for _, possibility := range possibilities { + if possibility == priority { + return possibility + } + } + } + + // GroupKind match + for _, priority := range preferences { + for _, possibility := range possibilities { + if possibility.GroupKind() == priority.GroupKind() { + return possibility + } + } + } + + // Group match + for _, priority := range preferences { + for _, possibility := range possibilities { + if possibility.Group == priority.Group { + return possibility + } + } + } + + // Just pick the first + return possibilities[0] +} diff --git a/pkg/kubectl/resource/visitor.go b/pkg/kubectl/resource/visitor.go index 10c8425d975..76569389c7d 100644 --- a/pkg/kubectl/resource/visitor.go +++ b/pkg/kubectl/resource/visitor.go @@ -26,6 +26,7 @@ import ( "path/filepath" "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" @@ -337,8 +338,15 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error { }{v.Mapper, v.Mapper.Decoder}); len(errs) > 0 { return utilerrors.NewAggregate(errs) } + + // If we have a GroupVersionKind on the list, prioritize that when asking for info on the objects contained in the list + var preferredGVKs []unversioned.GroupVersionKind + if info.Mapping != nil && !info.Mapping.GroupVersionKind.IsEmpty() { + preferredGVKs = append(preferredGVKs, info.Mapping.GroupVersionKind) + } + for i := range items { - item, err := v.InfoForObject(items[i]) + item, err := v.InfoForObject(items[i], preferredGVKs) if err != nil { return err }