mirror of https://github.com/k3s-io/k3s
Merge pull request #51727 from juanvallejo/jvallejo/ensure-unstructured-objects-kubectl-get
Automatic merge from submit-queue (batch tested with PRs 51031, 51705, 51888, 51727, 51684). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.. ensure unstructured resources in kubectl get Related downstream issue: https://github.com/openshift/origin/issues/16064 Ensure we are dealing with unstructured objects before attempting to compose into an unstructured list. `$ kubectl get ...` [assumes all resources returned from a resource builder are unstructured objects](https://github.com/kubernetes/kubernetes/blob/master/pkg/kubectl/cmd/get.go#L355). This leads to a panic when dealing with non-unstructured objects. This patch ensures we are dealing with unstructured objects before attempting to compose into an unstructured list. **Release note**: ```release-note NONE ``` cc @fabianofranz @kubernetes/sig-cli-miscpull/6/head
commit
2273f36818
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
@ -26,11 +27,13 @@ import (
|
|||
|
||||
kapierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
|
@ -354,17 +357,32 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||
var obj runtime.Object
|
||||
if !singleItemImplied || len(infos) > 1 {
|
||||
// we have more than one item, so coerce all items into a list
|
||||
list := &unstructured.UnstructuredList{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "List",
|
||||
"apiVersion": "v1",
|
||||
"metadata": map[string]interface{}{},
|
||||
// we have more than one item, so coerce all items into a list.
|
||||
// we don't want an *unstructured.Unstructured list yet, as we
|
||||
// may be dealing with non-unstructured objects. Compose all items
|
||||
// into an api.List, and then decode using an unstructured scheme.
|
||||
list := api.List{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "List",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ListMeta: metav1.ListMeta{},
|
||||
}
|
||||
for _, info := range infos {
|
||||
list.Items = append(list.Items, *info.Object.(*unstructured.Unstructured))
|
||||
list.Items = append(list.Items, info.Object)
|
||||
}
|
||||
obj = list
|
||||
|
||||
listData, err := json.Marshal(list)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, listData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj = converted
|
||||
} else {
|
||||
obj = infos[0].Object
|
||||
}
|
||||
|
|
|
@ -575,6 +575,83 @@ func TestGetListComponentStatus(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetMixedGenericObjects(t *testing.T) {
|
||||
initTestErrorHandler(t)
|
||||
|
||||
// ensure that a runtime.Object without
|
||||
// an ObjectMeta field is handled properly
|
||||
structuredObj := &metav1.Status{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Status",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
Status: "Success",
|
||||
Message: "",
|
||||
Reason: "",
|
||||
Code: 0,
|
||||
}
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{GenericPrinter: true}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch req.URL.Path {
|
||||
case "/namespaces/test/pods":
|
||||
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, structuredObj)}, nil
|
||||
default:
|
||||
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.Namespace = "test"
|
||||
tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(api.GroupName).GroupVersion}}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
errBuf := bytes.NewBuffer([]byte{})
|
||||
|
||||
cmd := NewCmdGet(f, buf, errBuf)
|
||||
cmd.SetOutput(buf)
|
||||
cmd.Flags().Set("output", "json")
|
||||
cmd.Run(cmd, []string{"pods"})
|
||||
|
||||
if len(buf.String()) == 0 {
|
||||
t.Error("unexpected empty output")
|
||||
}
|
||||
|
||||
actual := tf.Printer.(*testPrinter).Objects
|
||||
fn := func(obj runtime.Object) unstructured.Unstructured {
|
||||
data, err := runtime.Encode(api.Codecs.LegacyCodec(schema.GroupVersion{Version: "v1"}), obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
out := &unstructured.Unstructured{Object: make(map[string]interface{})}
|
||||
if err := encjson.Unmarshal(data, &out.Object); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return *out
|
||||
}
|
||||
|
||||
expected := &unstructured.UnstructuredList{
|
||||
Object: map[string]interface{}{"kind": "List", "apiVersion": "v1", "metadata": map[string]interface{}{"selfLink": "", "resourceVersion": ""}},
|
||||
Items: []unstructured.Unstructured{
|
||||
fn(structuredObj),
|
||||
},
|
||||
}
|
||||
actualBytes, err := encjson.Marshal(actual[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectedBytes, err := encjson.Marshal(expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(actualBytes) != string(expectedBytes) {
|
||||
t.Errorf("expectedBytes: %s,but actualBytes: %s", expectedBytes, actualBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMultipleTypeObjects(t *testing.T) {
|
||||
pods, svc, _ := testData()
|
||||
|
||||
|
@ -649,11 +726,11 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
|
|||
fn := func(obj runtime.Object) unstructured.Unstructured {
|
||||
data, err := runtime.Encode(api.Codecs.LegacyCodec(schema.GroupVersion{Version: "v1"}), obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
out := &unstructured.Unstructured{Object: make(map[string]interface{})}
|
||||
if err := encjson.Unmarshal(data, &out.Object); err != nil {
|
||||
panic(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
return *out
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue