update -o name format to kind.group/name

This commit is contained in:
juanvallejo 2018-02-05 11:21:00 -05:00
parent 76e6da25fa
commit 765f9ec68b
No known key found for this signature in database
GPG Key ID: 7D2C958002D6448D
17 changed files with 178 additions and 62 deletions

View File

@ -1460,7 +1460,7 @@ run_kubectl_get_tests() {
kube::test::if_has_string "${output_message}" 'valid-pod' # pod details kube::test::if_has_string "${output_message}" 'valid-pod' # pod details
output_message=$(kubectl get pods/valid-pod -o name -w --request-timeout=1 "${kube_flags[@]}") output_message=$(kubectl get pods/valid-pod -o name -w --request-timeout=1 "${kube_flags[@]}")
kube::test::if_has_not_string "${output_message}" 'STATUS' # no headers kube::test::if_has_not_string "${output_message}" 'STATUS' # no headers
kube::test::if_has_string "${output_message}" 'pods/valid-pod' # resource name kube::test::if_has_string "${output_message}" 'pod/valid-pod' # resource name
output_message=$(kubectl get pods/valid-pod -o yaml -w --request-timeout=1 "${kube_flags[@]}") output_message=$(kubectl get pods/valid-pod -o yaml -w --request-timeout=1 "${kube_flags[@]}")
kube::test::if_has_not_string "${output_message}" 'STATUS' # no headers kube::test::if_has_not_string "${output_message}" 'STATUS' # no headers
kube::test::if_has_string "${output_message}" 'name: valid-pod' # yaml kube::test::if_has_string "${output_message}" 'name: valid-pod' # yaml
@ -1574,6 +1574,33 @@ __EOF__
# Post-Condition: assertion object exist # Post-Condition: assertion object exist
kube::test::get_object_assert customresourcedefinitions "{{range.items}}{{$id_field}}:{{end}}" 'bars.company.com:foos.company.com:' kube::test::get_object_assert customresourcedefinitions "{{range.items}}{{$id_field}}:{{end}}" 'bars.company.com:foos.company.com:'
# This test ensures that the name printer is able to output a resource
# in the proper "kind.group/resource_name" format, and that the
# resource builder is able to resolve a GVK when a kind.group pair is given.
kubectl "${kube_flags_with_token[@]}" create -f - << __EOF__
{
"kind": "CustomResourceDefinition",
"apiVersion": "apiextensions.k8s.io/v1beta1",
"metadata": {
"name": "resources.mygroup.example.com"
},
"spec": {
"group": "mygroup.example.com",
"version": "v1alpha1",
"scope": "Namespaced",
"names": {
"plural": "resources",
"singular": "resource",
"kind": "Kind",
"listKind": "KindList"
}
}
}
__EOF__
# Post-Condition: assertion crd with non-matching kind and resource exists
kube::test::get_object_assert customresourcedefinitions "{{range.items}}{{$id_field}}:{{end}}" 'bars.company.com:foos.company.com:resources.mygroup.example.com:'
run_non_native_resource_tests run_non_native_resource_tests
# teardown # teardown
@ -1621,6 +1648,28 @@ run_non_native_resource_tests() {
# Test that we can list this new CustomResource (bars) # Test that we can list this new CustomResource (bars)
kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" '' kube::test::get_object_assert bars "{{range.items}}{{$id_field}}:{{end}}" ''
# Test that we can list this new CustomResource (resources)
kube::test::get_object_assert resources "{{range.items}}{{$id_field}}:{{end}}" ''
# Test that we can create a new resource of type Kind
kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/resource.yaml "${kube_flags[@]}"
# Test that -o name returns kind.group/resourcename
output_message=$(kubectl "${kube_flags[@]}" get resource/myobj -o name)
kube::test::if_has_string "${output_message}" 'kind.mygroup.example.com/myobj'
output_message=$(kubectl "${kube_flags[@]}" get resources/myobj -o name)
kube::test::if_has_string "${output_message}" 'kind.mygroup.example.com/myobj'
output_message=$(kubectl "${kube_flags[@]}" get kind.mygroup.example.com/myobj -o name)
kube::test::if_has_string "${output_message}" 'kind.mygroup.example.com/myobj'
# Delete the resource with cascade.
kubectl "${kube_flags[@]}" delete resources myobj --cascade=true
# Make sure it's gone
kube::test::get_object_assert resources "{{range.items}}{{$id_field}}:{{end}}" ''
# Test that we can create a new resource of type Foo # Test that we can create a new resource of type Foo
kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/foo.yaml "${kube_flags[@]}" kubectl "${kube_flags[@]}" create -f hack/testdata/CRD/foo.yaml "${kube_flags[@]}"
@ -1649,7 +1698,7 @@ run_non_native_resource_tests() {
kubectl "${kube_flags[@]}" get foos -o "go-template={{range .items}}{{.someField}}{{end}}" --allow-missing-template-keys=false kubectl "${kube_flags[@]}" get foos -o "go-template={{range .items}}{{.someField}}{{end}}" --allow-missing-template-keys=false
kubectl "${kube_flags[@]}" get foos/test -o "go-template={{.someField}}" --allow-missing-template-keys=false kubectl "${kube_flags[@]}" get foos/test -o "go-template={{.someField}}" --allow-missing-template-keys=false
output_message=$(kubectl "${kube_flags[@]}" get foos/test -o name) output_message=$(kubectl "${kube_flags[@]}" get foos/test -o name)
kube::test::if_has_string "${output_message}" 'foos/test' kube::test::if_has_string "${output_message}" 'foo.company.com/test'
# Test patching # Test patching
kube::log::status "Testing CustomResource patching" kube::log::status "Testing CustomResource patching"
@ -1732,7 +1781,7 @@ run_non_native_resource_tests() {
# Stop the watcher and the patch loop. # Stop the watcher and the patch loop.
kill -9 ${watch_pid} kill -9 ${watch_pid}
kill -9 ${patch_pid} kill -9 ${patch_pid}
kube::test::if_has_string "${watch_output}" 'bars/test' kube::test::if_has_string "${watch_output}" 'bar.company.com/test'
# Delete the resource without cascade. # Delete the resource without cascade.
kubectl "${kube_flags[@]}" delete bars test --cascade=false kubectl "${kube_flags[@]}" delete bars test --cascade=false

6
hack/testdata/CRD/resource.yaml vendored Normal file
View File

@ -0,0 +1,6 @@
apiVersion: mygroup.example.com/v1alpha1
kind: Kind
metadata:
name: myobj
spec:
key: value

View File

@ -287,7 +287,7 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error {
return nil return nil
} }
if o.outputFormat != "" { if len(o.outputFormat) > 0 {
return f.PrintObject(cmd, o.local, r.Mapper().RESTMapper, outputObj, o.out) return f.PrintObject(cmd, o.local, r.Mapper().RESTMapper, outputObj, o.out)
} }
f.PrintSuccess(false, o.out, info.Mapping.Resource, info.Name, o.dryrun, dataChangeMsg) f.PrintSuccess(false, o.out, info.Mapping.Resource, info.Name, o.dryrun, dataChangeMsg)

View File

@ -64,8 +64,8 @@ func TestSetEnvLocal(t *testing.T) {
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", "name")
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
mapper, typer := f.Object() _, typer := f.Object()
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper} tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer}
opts := EnvOptions{FilenameOptions: resource.FilenameOptions{ opts := EnvOptions{FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../examples/storage/cassandra/cassandra-controller.yaml"}}, Filenames: []string{"../../../../examples/storage/cassandra/cassandra-controller.yaml"}},
@ -78,7 +78,7 @@ func TestSetEnvLocal(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !strings.Contains(buf.String(), "replicationcontrollers/cassandra") { if !strings.Contains(buf.String(), "replicationcontroller/cassandra") {
t.Errorf("did not set env: %s", buf.String()) t.Errorf("did not set env: %s", buf.String())
} }
} }
@ -101,8 +101,8 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) {
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", "name")
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
mapper, typer := f.Object() _, typer := f.Object()
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper} tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer}
opts := EnvOptions{FilenameOptions: resource.FilenameOptions{ opts := EnvOptions{FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}}, Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}},
@ -116,7 +116,7 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
expectedOut := "replicationcontrollers/first-rc\nreplicationcontrollers/second-rc\n" expectedOut := "replicationcontroller/first-rc\nreplicationcontroller/second-rc\n"
if buf.String() != expectedOut { if buf.String() != expectedOut {
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String()) t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
} }

View File

@ -63,8 +63,8 @@ func TestImageLocal(t *testing.T) {
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", "name")
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
mapper, typer := f.Object() _, typer := f.Object()
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper} tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer}
opts := ImageOptions{FilenameOptions: resource.FilenameOptions{ opts := ImageOptions{FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../examples/storage/cassandra/cassandra-controller.yaml"}}, Filenames: []string{"../../../../examples/storage/cassandra/cassandra-controller.yaml"}},
@ -80,7 +80,7 @@ func TestImageLocal(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !strings.Contains(buf.String(), "replicationcontrollers/cassandra") { if !strings.Contains(buf.String(), "replicationcontroller/cassandra") {
t.Errorf("did not set image: %s", buf.String()) t.Errorf("did not set image: %s", buf.String())
} }
} }
@ -166,8 +166,8 @@ func TestSetMultiResourcesImageLocal(t *testing.T) {
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", "name")
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
mapper, typer := f.Object() _, typer := f.Object()
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper} tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer}
opts := ImageOptions{FilenameOptions: resource.FilenameOptions{ opts := ImageOptions{FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}}, Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}},
@ -183,7 +183,7 @@ func TestSetMultiResourcesImageLocal(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
expectedOut := "replicationcontrollers/first-rc\nreplicationcontrollers/second-rc\n" expectedOut := "replicationcontroller/first-rc\nreplicationcontroller/second-rc\n"
if buf.String() != expectedOut { if buf.String() != expectedOut {
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String()) t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
} }

View File

@ -63,8 +63,8 @@ func TestResourcesLocal(t *testing.T) {
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", "name")
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
mapper, typer := f.Object() _, typer := f.Object()
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper} tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer}
opts := ResourcesOptions{FilenameOptions: resource.FilenameOptions{ opts := ResourcesOptions{FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../examples/storage/cassandra/cassandra-controller.yaml"}}, Filenames: []string{"../../../../examples/storage/cassandra/cassandra-controller.yaml"}},
@ -84,7 +84,7 @@ func TestResourcesLocal(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !strings.Contains(buf.String(), "replicationcontrollers/cassandra") { if !strings.Contains(buf.String(), "replicationcontroller/cassandra") {
t.Errorf("did not set resources: %s", buf.String()) t.Errorf("did not set resources: %s", buf.String())
} }
} }
@ -107,8 +107,8 @@ func TestSetMultiResourcesLimitsLocal(t *testing.T) {
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", "name")
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
mapper, typer := f.Object() _, typer := f.Object()
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper} tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer}
opts := ResourcesOptions{FilenameOptions: resource.FilenameOptions{ opts := ResourcesOptions{FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}}, Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}},
@ -128,7 +128,7 @@ func TestSetMultiResourcesLimitsLocal(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
expectedOut := "replicationcontrollers/first-rc\nreplicationcontrollers/second-rc\n" expectedOut := "replicationcontroller/first-rc\nreplicationcontroller/second-rc\n"
if buf.String() != expectedOut { if buf.String() != expectedOut {
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String()) t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
} }
@ -448,8 +448,8 @@ func TestSetResourcesRemote(t *testing.T) {
testapi.Default = testapi.Groups[input.testAPIGroup] testapi.Default = testapi.Groups[input.testAPIGroup]
f, tf, _, ns := cmdtesting.NewAPIFactory() f, tf, _, ns := cmdtesting.NewAPIFactory()
codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion) codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion)
mapper, typer := f.Object() _, typer := f.Object()
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{testapi.Default.Codec()}, Typer: typer, Mapper: mapper} tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{testapi.Default.Codec()}, Typer: typer}
tf.Namespace = "test" tf.Namespace = "test"
tf.CategoryExpander = categories.LegacyCategoryExpander tf.CategoryExpander = categories.LegacyCategoryExpander
tf.Client = &fake.RESTClient{ tf.Client = &fake.RESTClient{

View File

@ -335,11 +335,11 @@ func TestSelectorTest(t *testing.T) {
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
cmd.Flags().Set("filename", "../../../../examples/storage/cassandra/cassandra-service.yaml") cmd.Flags().Set("filename", "../../../../examples/storage/cassandra/cassandra-service.yaml")
mapper, typer := f.Object() _, typer := f.Object()
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper} tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer}
cmd.Run(cmd, []string{"environment=qa"}) cmd.Run(cmd, []string{"environment=qa"})
if !strings.Contains(buf.String(), "services/cassandra") { if !strings.Contains(buf.String(), "service/cassandra") {
t.Errorf("did not set selector: %s", buf.String()) t.Errorf("did not set selector: %s", buf.String())
} }
} }

View File

@ -116,8 +116,8 @@ func TestSetServiceAccountMultiLocal(t *testing.T) {
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", "name")
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
mapper, typer := f.Object() _, typer := f.Object()
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper} tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer}
opts := serviceAccountConfig{fileNameOptions: resource.FilenameOptions{ opts := serviceAccountConfig{fileNameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}}, Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}},
out: buf, out: buf,
@ -130,7 +130,7 @@ func TestSetServiceAccountMultiLocal(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
expectedOut := "replicationcontrollers/first-rc\nreplicationcontrollers/second-rc\n" expectedOut := "replicationcontroller/first-rc\nreplicationcontroller/second-rc\n"
if buf.String() != expectedOut { if buf.String() != expectedOut {
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String()) t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
} }

View File

@ -48,15 +48,12 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac
} }
func (f *ring2Factory) PrinterForOptions(options *printers.PrintOptions) (printers.ResourcePrinter, error) { func (f *ring2Factory) PrinterForOptions(options *printers.PrintOptions) (printers.ResourcePrinter, error) {
var mapper meta.RESTMapper _, typer := f.objectMappingFactory.Object()
var typer runtime.ObjectTyper
mapper, typer = f.objectMappingFactory.Object()
// TODO: used by the custom column implementation and the name implementation, break this dependency // TODO: used by the custom column implementation and the name implementation, break this dependency
decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme} decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme}
encoder := f.clientAccessFactory.JSONEncoder() encoder := f.clientAccessFactory.JSONEncoder()
return printerForOptions(mapper, typer, encoder, decoders, options) return printerForOptions(typer, encoder, decoders, options)
} }
func (f *ring2Factory) PrinterForMapping(options *printers.PrintOptions, mapping *meta.RESTMapping) (printers.ResourcePrinter, error) { func (f *ring2Factory) PrinterForMapping(options *printers.PrintOptions, mapping *meta.RESTMapping) (printers.ResourcePrinter, error) {

View File

@ -20,7 +20,6 @@ import (
"fmt" "fmt"
"strings" "strings"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
@ -84,8 +83,8 @@ func ValidateOutputArgs(cmd *cobra.Command) error {
// printerForOptions returns the printer for the outputOptions (if given) or // printerForOptions returns the printer for the outputOptions (if given) or
// returns the default printer for the command. Requires that printer flags have // returns the default printer for the command. Requires that printer flags have
// been added to cmd (see AddPrinterFlags). // been added to cmd (see AddPrinterFlags).
func printerForOptions(mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options *printers.PrintOptions) (printers.ResourcePrinter, error) { func printerForOptions(typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options *printers.PrintOptions) (printers.ResourcePrinter, error) {
printer, err := printers.GetStandardPrinter(mapper, typer, encoder, decoders, *options) printer, err := printers.GetStandardPrinter(typer, encoder, decoders, *options)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -598,10 +598,27 @@ func (b *Builder) SingleResourceType() *Builder {
return b return b
} }
// mappingFor returns the RESTMapping for the Kind referenced by the resource. // mappingFor returns the RESTMapping for the Kind given, or the Kind referenced by the resource.
// prefers a fully specified GroupVersionResource match. If we don't have one match on GroupResource // prefers a fully specified GroupVersionKind match. If we don't have one, match on a fully specified
func (b *Builder) mappingFor(resourceArg string) (*meta.RESTMapping, error) { // GroupVersionResource, or fallback to a match on GroupResource.
fullySpecifiedGVR, groupResource := schema.ParseResourceArg(resourceArg) func (b *Builder) mappingFor(resourceOrKindArg string) (*meta.RESTMapping, error) {
fullySpecifiedGVK, groupKind := schema.ParseKindArg(resourceOrKindArg)
if fullySpecifiedGVK == nil {
gvk := groupKind.WithVersion("")
fullySpecifiedGVK = &gvk
}
if !fullySpecifiedGVK.Empty() {
if mapping, err := b.mapper.RESTMapping(fullySpecifiedGVK.GroupKind(), fullySpecifiedGVK.Version); err == nil {
return mapping, nil
} else {
if mapping, err := b.mapper.RESTMapping(groupKind, ""); err == nil {
return mapping, nil
}
}
}
fullySpecifiedGVR, groupResource := schema.ParseResourceArg(resourceOrKindArg)
gvk := schema.GroupVersionKind{} gvk := schema.GroupVersionKind{}
if fullySpecifiedGVR != nil { if fullySpecifiedGVR != nil {
gvk, _ = b.mapper.KindFor(*fullySpecifiedGVR) gvk, _ = b.mapper.KindFor(*fullySpecifiedGVR)

View File

@ -26,6 +26,7 @@ go_library(
"//vendor/github.com/ghodss/yaml:go_default_library", "//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",

View File

@ -108,7 +108,7 @@ func TestPrintDefault(t *testing.T) {
} }
for _, test := range printerTests { for _, test := range printerTests {
printer, err := printers.GetStandardPrinter(nil, nil, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{AllowMissingKeys: false}) printer, err := printers.GetStandardPrinter(nil, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{AllowMissingKeys: false})
if err != nil { if err != nil {
t.Errorf("in %s, unexpected error: %#v", test.Name, err) t.Errorf("in %s, unexpected error: %#v", test.Name, err)
} }
@ -278,12 +278,12 @@ func TestPrinter(t *testing.T) {
{"test jsonpath", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.metadata.name}", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"}, {"test jsonpath", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.metadata.name}", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"},
{"test jsonpath list", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.items[*].metadata.name}", AllowMissingKeys: true}, podListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo bar"}, {"test jsonpath list", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.items[*].metadata.name}", AllowMissingKeys: true}, podListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo bar"},
{"test jsonpath empty list", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.items[*].metadata.name}", AllowMissingKeys: true}, emptyListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, ""}, {"test jsonpath empty list", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.items[*].metadata.name}", AllowMissingKeys: true}, emptyListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, ""},
{"test name", &printers.PrintOptions{OutputFormatType: "name", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pods/foo\n"}, {"test name", &printers.PrintOptions{OutputFormatType: "name", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pod/foo\n"},
{"emits versioned objects", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{.kind}}", AllowMissingKeys: true}, testapi, []schema.GroupVersion{v1.SchemeGroupVersion}, "Pod"}, {"emits versioned objects", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{.kind}}", AllowMissingKeys: true}, testapi, []schema.GroupVersion{v1.SchemeGroupVersion}, "Pod"},
} }
for _, test := range printerTests { for _, test := range printerTests {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
printer, err := printers.GetStandardPrinter(legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts) printer, err := printers.GetStandardPrinter(legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts)
if err != nil { if err != nil {
t.Errorf("in %s, unexpected error: %#v", test.Name, err) t.Errorf("in %s, unexpected error: %#v", test.Name, err)
} }
@ -313,7 +313,7 @@ func TestBadPrinter(t *testing.T) {
{"unknown format", &printers.PrintOptions{OutputFormatType: "anUnknownFormat", OutputFormatArgument: "", AllowMissingKeys: false}, fmt.Errorf("output format \"anUnknownFormat\" not recognized")}, {"unknown format", &printers.PrintOptions{OutputFormatType: "anUnknownFormat", OutputFormatArgument: "", AllowMissingKeys: false}, fmt.Errorf("output format \"anUnknownFormat\" not recognized")},
} }
for _, test := range badPrinterTests { for _, test := range badPrinterTests {
_, err := printers.GetStandardPrinter(legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts) _, err := printers.GetStandardPrinter(legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts)
if err == nil || err.Error() != test.Error.Error() { if err == nil || err.Error() != test.Error.Error() {
t.Errorf("in %s, expect %s, got %s", test.Name, test.Error, err) t.Errorf("in %s, expect %s, got %s", test.Name, test.Error, err)
} }
@ -489,7 +489,7 @@ func TestNamePrinter(t *testing.T) {
Name: "foo", Name: "foo",
}, },
}, },
"pods/foo\n"}, "pod/foo\n"},
"List": { "List": {
&v1.List{ &v1.List{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
@ -504,10 +504,10 @@ func TestNamePrinter(t *testing.T) {
}, },
}, },
}, },
"pods/foo\npods/bar\n"}, "pod/foo\npod/bar\n"},
} }
printOpts := &printers.PrintOptions{OutputFormatType: "name", AllowMissingKeys: false} printOpts := &printers.PrintOptions{OutputFormatType: "name", AllowMissingKeys: false}
printer, _ := printers.GetStandardPrinter(legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *printOpts) printer, _ := printers.GetStandardPrinter(legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *printOpts)
for name, item := range tests { for name, item := range tests {
buff := &bytes.Buffer{} buff := &bytes.Buffer{}
err := printer.PrintObj(item.obj, buff) err := printer.PrintObj(item.obj, buff)
@ -682,7 +682,6 @@ func TestPrinters(t *testing.T) {
"name": &printers.NamePrinter{ "name": &printers.NamePrinter{
Typer: legacyscheme.Scheme, Typer: legacyscheme.Scheme,
Decoders: []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, Decoders: []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme},
Mapper: legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...),
}, },
} }
AddHandlers((allPrinters["humanReadable"]).(*printers.HumanReadablePrinter)) AddHandlers((allPrinters["humanReadable"]).(*printers.HumanReadablePrinter))
@ -2819,7 +2818,7 @@ func TestAllowMissingKeys(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
printer, err := printers.GetStandardPrinter(legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts) printer, err := printers.GetStandardPrinter(legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts)
if err != nil { if err != nil {
t.Errorf("in %s, unexpected error: %#v", test.Name, err) t.Errorf("in %s, unexpected error: %#v", test.Name, err)
} }

View File

@ -19,8 +19,10 @@ package printers
import ( import (
"fmt" "fmt"
"io" "io"
"strings"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
) )
@ -29,7 +31,6 @@ import (
type NamePrinter struct { type NamePrinter struct {
Decoders []runtime.Decoder Decoders []runtime.Decoder
Typer runtime.ObjectTyper Typer runtime.ObjectTyper
Mapper meta.RESTMapper
} }
func (p *NamePrinter) AfterPrint(w io.Writer, res string) error { func (p *NamePrinter) AfterPrint(w io.Writer, res string) error {
@ -64,18 +65,27 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
groupVersionKind := obj.GetObjectKind().GroupVersionKind() groupVersionKind := obj.GetObjectKind().GroupVersionKind()
if len(groupVersionKind.Kind) > 0 { if len(groupVersionKind.Kind) > 0 {
if mappings, err := p.Mapper.RESTMappings(groupVersionKind.GroupKind(), groupVersionKind.Version); err == nil && len(mappings) > 0 { kind := groupVersionKind.Kind
fmt.Fprintf(w, "%s/%s\n", mappings[0].Resource, name) group := groupVersionKind.Group
return nil return printObj(w, name, group, kind)
}
} }
if gvks, _, err := p.Typer.ObjectKinds(obj); err == nil { if gvks, _, err := p.Typer.ObjectKinds(obj); err == nil {
for _, gvk := range gvks { for _, gvk := range gvks {
if mappings, err := p.Mapper.RESTMappings(gvk.GroupKind(), gvk.Version); err == nil && len(mappings) > 0 { if len(gvk.Kind) == 0 {
fmt.Fprintf(w, "%s/%s\n", mappings[0].Resource, name) continue
return nil
} }
return printObj(w, name, gvk.Group, gvk.Kind)
}
}
if uns, ok := obj.(*unstructured.Unstructured); ok {
group := uns.GroupVersionKind().Group
kind := uns.GroupVersionKind().Kind
if len(kind) > 0 {
return printObj(w, name, group, kind)
} }
} }
@ -83,6 +93,20 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
return nil return nil
} }
func printObj(w io.Writer, name, group, kind string) error {
if len(kind) == 0 {
return fmt.Errorf("missing kind for resource with name %v", name)
}
if len(group) == 0 {
fmt.Fprintf(w, "%s/%s\n", strings.ToLower(kind), name)
return nil
}
fmt.Fprintf(w, "%s.%s/%s\n", strings.ToLower(kind), group, name)
return nil
}
// TODO: implement HandledResources() // TODO: implement HandledResources()
func (p *NamePrinter) HandledResources() []string { func (p *NamePrinter) HandledResources() []string {
return []string{} return []string{}

View File

@ -21,7 +21,6 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
) )
@ -29,7 +28,7 @@ import (
// a printer or an error. The printer is agnostic to schema versions, so you must // a printer or an error. The printer is agnostic to schema versions, so you must
// send arguments to PrintObj in the version you wish them to be shown using a // send arguments to PrintObj in the version you wish them to be shown using a
// VersionedPrinter (typically when generic is true). // VersionedPrinter (typically when generic is true).
func GetStandardPrinter(mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options PrintOptions) (ResourcePrinter, error) { func GetStandardPrinter(typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options PrintOptions) (ResourcePrinter, error) {
format, formatArgument, allowMissingTemplateKeys := options.OutputFormatType, options.OutputFormatArgument, options.AllowMissingKeys format, formatArgument, allowMissingTemplateKeys := options.OutputFormatType, options.OutputFormatArgument, options.AllowMissingKeys
var printer ResourcePrinter var printer ResourcePrinter
@ -45,7 +44,6 @@ func GetStandardPrinter(mapper meta.RESTMapper, typer runtime.ObjectTyper, encod
printer = &NamePrinter{ printer = &NamePrinter{
Typer: typer, Typer: typer,
Decoders: decoders, Decoders: decoders,
Mapper: mapper,
} }
case "template", "go-template": case "template", "go-template":

View File

@ -36,6 +36,21 @@ func ParseResourceArg(arg string) (*GroupVersionResource, GroupResource) {
return gvr, ParseGroupResource(arg) return gvr, ParseGroupResource(arg)
} }
// ParseKindArg takes the common style of string which may be either `Kind.group.com` or `Kind.version.group.com`
// and parses it out into both possibilities. This code takes no responsibility for knowing which representation was intended
// but with a knowledge of all GroupKinds, calling code can take a very good guess. If there are only two segments, then
// `*GroupVersionResource` is nil.
// `Kind.group.com` -> `group=com, version=group, kind=Kind` and `group=group.com, kind=Kind`
func ParseKindArg(arg string) (*GroupVersionKind, GroupKind) {
var gvk *GroupVersionKind
if strings.Count(arg, ".") >= 2 {
s := strings.SplitN(arg, ".", 3)
gvk = &GroupVersionKind{Group: s[2], Version: s[1], Kind: s[0]}
}
return gvk, ParseGroupKind(arg)
}
// GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying // GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying
// concepts during lookup stages without having partially valid types // concepts during lookup stages without having partially valid types
type GroupResource struct { type GroupResource struct {
@ -58,6 +73,15 @@ func (gr *GroupResource) String() string {
return gr.Resource + "." + gr.Group return gr.Resource + "." + gr.Group
} }
func ParseGroupKind(gk string) GroupKind {
i := strings.Index(gk, ".")
if i == -1 {
return GroupKind{Kind: gk}
}
return GroupKind{Group: gk[i+1:], Kind: gk[:i]}
}
// ParseGroupResource turns "resource.group" string into a GroupResource struct. Empty strings are allowed // ParseGroupResource turns "resource.group" string into a GroupResource struct. Empty strings are allowed
// for each field. // for each field.
func ParseGroupResource(gr string) GroupResource { func ParseGroupResource(gr string) GroupResource {

View File

@ -18,6 +18,7 @@ package discovery
import ( import (
"fmt" "fmt"
"strings"
"sync" "sync"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
@ -108,6 +109,7 @@ func NewRESTMapper(groupResources []*APIGroupResources, versionInterfaces meta.V
plural := gv.WithResource(resource.Name) plural := gv.WithResource(resource.Name)
singular := gv.WithResource(resource.SingularName) singular := gv.WithResource(resource.SingularName)
versionMapper.AddSpecific(gv.WithKind(resource.Kind), plural, singular, scope) versionMapper.AddSpecific(gv.WithKind(resource.Kind), plural, singular, scope)
versionMapper.AddSpecific(gv.WithKind(strings.ToLower(resource.Kind)), plural, singular, scope)
// TODO this is producing unsafe guesses that don't actually work, but it matches previous behavior // TODO this is producing unsafe guesses that don't actually work, but it matches previous behavior
versionMapper.Add(gv.WithKind(resource.Kind+"List"), scope) versionMapper.Add(gv.WithKind(resource.Kind+"List"), scope)
} }