k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/apimachinery/table_conversion.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package apimachinery 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "text/tabwriter" 24 25 "github.com/onsi/ginkgo/v2" 26 "github.com/onsi/gomega" 27 28 authorizationv1 "k8s.io/api/authorization/v1" 29 v1 "k8s.io/api/core/v1" 30 apierrors "k8s.io/apimachinery/pkg/api/errors" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" 33 "k8s.io/client-go/util/workqueue" 34 admissionapi "k8s.io/pod-security-admission/api" 35 36 utilversion "k8s.io/apimachinery/pkg/util/version" 37 "k8s.io/cli-runtime/pkg/printers" 38 "k8s.io/kubernetes/test/e2e/framework" 39 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 40 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" 41 ) 42 43 var serverPrintVersion = utilversion.MustParseSemantic("v1.10.0") 44 45 var _ = SIGDescribe("Servers with support for Table transformation", func() { 46 f := framework.NewDefaultFramework("tables") 47 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 48 49 ginkgo.BeforeEach(func() { 50 e2eskipper.SkipUnlessServerVersionGTE(serverPrintVersion, f.ClientSet.Discovery()) 51 }) 52 53 ginkgo.It("should return pod details", func(ctx context.Context) { 54 ns := f.Namespace.Name 55 c := f.ClientSet 56 57 podName := "pod-1" 58 framework.Logf("Creating pod %s", podName) 59 60 _, err := c.CoreV1().Pods(ns).Create(ctx, newTablePod(ns, podName), metav1.CreateOptions{}) 61 framework.ExpectNoError(err, "failed to create pod %s in namespace: %s", podName, ns) 62 63 table := &metav1beta1.Table{} 64 err = c.CoreV1().RESTClient().Get().Resource("pods").Namespace(ns).Name(podName).SetHeader("Accept", "application/json;as=Table;v=v1beta1;g=meta.k8s.io").Do(ctx).Into(table) 65 framework.ExpectNoError(err, "failed to get pod %s in Table form in namespace: %s", podName, ns) 66 framework.Logf("Table: %#v", table) 67 68 gomega.Expect(len(table.ColumnDefinitions)).To(gomega.BeNumerically(">", 2)) 69 gomega.Expect(table.Rows).To(gomega.HaveLen(1)) 70 gomega.Expect(table.Rows[0].Cells).To(gomega.HaveLen(len(table.ColumnDefinitions))) 71 gomega.Expect(table.ColumnDefinitions[0].Name).To(gomega.Equal("Name")) 72 gomega.Expect(table.Rows[0].Cells[0]).To(gomega.Equal(podName)) 73 74 out := printTable(table) 75 gomega.Expect(out).To(gomega.MatchRegexp("^NAME\\s")) 76 gomega.Expect(out).To(gomega.MatchRegexp("\npod-1\\s")) 77 framework.Logf("Table:\n%s", out) 78 }) 79 80 ginkgo.It("should return chunks of table results for list calls", func(ctx context.Context) { 81 ns := f.Namespace.Name 82 c := f.ClientSet 83 client := c.CoreV1().PodTemplates(ns) 84 85 ginkgo.By("creating a large number of resources") 86 workqueue.ParallelizeUntil(ctx, 5, 20, func(i int) { 87 for tries := 3; tries >= 0; tries-- { 88 _, err := client.Create(ctx, &v1.PodTemplate{ 89 ObjectMeta: metav1.ObjectMeta{ 90 Name: fmt.Sprintf("template-%04d", i), 91 }, 92 Template: v1.PodTemplateSpec{ 93 Spec: v1.PodSpec{ 94 Containers: []v1.Container{ 95 {Name: "test", Image: "test2"}, 96 }, 97 }, 98 }, 99 }, metav1.CreateOptions{}) 100 if err == nil { 101 return 102 } 103 framework.Logf("Got an error creating template %d: %v", i, err) 104 } 105 framework.Failf("Unable to create template %d, exiting", i) 106 }) 107 108 pagedTable := &metav1beta1.Table{} 109 err := c.CoreV1().RESTClient().Get().Namespace(ns).Resource("podtemplates"). 110 VersionedParams(&metav1.ListOptions{Limit: 2}, metav1.ParameterCodec). 111 SetHeader("Accept", "application/json;as=Table;v=v1beta1;g=meta.k8s.io"). 112 Do(ctx).Into(pagedTable) 113 framework.ExpectNoError(err, "failed to get pod templates in Table form in namespace: %s", ns) 114 gomega.Expect(pagedTable.Rows).To(gomega.HaveLen(2)) 115 gomega.Expect(pagedTable.ResourceVersion).ToNot(gomega.BeEmpty()) 116 gomega.Expect(pagedTable.Continue).ToNot(gomega.BeEmpty()) 117 gomega.Expect(pagedTable.Rows[0].Cells[0]).To(gomega.Equal("template-0000")) 118 gomega.Expect(pagedTable.Rows[1].Cells[0]).To(gomega.Equal("template-0001")) 119 120 err = c.CoreV1().RESTClient().Get().Namespace(ns).Resource("podtemplates"). 121 VersionedParams(&metav1.ListOptions{Continue: pagedTable.Continue}, metav1.ParameterCodec). 122 SetHeader("Accept", "application/json;as=Table;v=v1beta1;g=meta.k8s.io"). 123 Do(ctx).Into(pagedTable) 124 framework.ExpectNoError(err, "failed to get pod templates in Table form in namespace: %s", ns) 125 gomega.Expect(pagedTable.Rows).ToNot(gomega.BeEmpty()) 126 gomega.Expect(pagedTable.Rows[0].Cells[0]).To(gomega.Equal("template-0002")) 127 }) 128 129 ginkgo.It("should return generic metadata details across all namespaces for nodes", func(ctx context.Context) { 130 c := f.ClientSet 131 132 table := &metav1beta1.Table{} 133 err := c.CoreV1().RESTClient().Get().Resource("nodes").SetHeader("Accept", "application/json;as=Table;v=v1beta1;g=meta.k8s.io").Do(ctx).Into(table) 134 framework.ExpectNoError(err, "failed to get nodes in Table form across all namespaces") 135 framework.Logf("Table: %#v", table) 136 137 gomega.Expect(len(table.ColumnDefinitions)).To(gomega.BeNumerically(">=", 2)) 138 gomega.Expect(table.Rows).ToNot(gomega.BeEmpty()) 139 gomega.Expect(table.Rows[0].Cells).To(gomega.HaveLen(len(table.ColumnDefinitions))) 140 gomega.Expect(table.ColumnDefinitions[0].Name).To(gomega.Equal("Name")) 141 gomega.Expect(table.ResourceVersion).ToNot(gomega.BeEmpty()) 142 143 out := printTable(table) 144 gomega.Expect(out).To(gomega.MatchRegexp("^NAME\\s")) 145 framework.Logf("Table:\n%s", out) 146 }) 147 148 /* 149 Release: v1.16 150 Testname: API metadata HTTP return 151 Description: Issue a HTTP request to the API. 152 HTTP request MUST return a HTTP status code of 406. 153 */ 154 framework.ConformanceIt("should return a 406 for a backend which does not implement metadata", func(ctx context.Context) { 155 c := f.ClientSet 156 157 table := &metav1beta1.Table{} 158 sar := &authorizationv1.SelfSubjectAccessReview{ 159 Spec: authorizationv1.SelfSubjectAccessReviewSpec{ 160 NonResourceAttributes: &authorizationv1.NonResourceAttributes{ 161 Path: "/", 162 Verb: "get", 163 }, 164 }, 165 } 166 err := c.AuthorizationV1().RESTClient().Post().Resource("selfsubjectaccessreviews").SetHeader("Accept", "application/json;as=Table;v=v1;g=meta.k8s.io").Body(sar).Do(ctx).Into(table) 167 gomega.Expect(err).To(gomega.HaveOccurred(), "failed to return error when posting self subject access review: %+v, to a backend that does not implement metadata", sar) 168 gomega.Expect(err.(apierrors.APIStatus)).To(gomega.HaveField("Status().Code", gomega.Equal(int32(406)))) 169 }) 170 }) 171 172 func printTable(table *metav1beta1.Table) string { 173 buf := &bytes.Buffer{} 174 tw := tabwriter.NewWriter(buf, 5, 8, 1, ' ', 0) 175 printer := printers.NewTablePrinter(printers.PrintOptions{}) 176 err := printer.PrintObj(table, tw) 177 framework.ExpectNoError(err, "failed to print table: %+v", table) 178 tw.Flush() 179 return buf.String() 180 } 181 182 func newTablePod(ns, podName string) *v1.Pod { 183 port := 8080 184 pod := e2epod.NewAgnhostPod(ns, podName, nil, nil, []v1.ContainerPort{{ContainerPort: int32(port)}}, "porter") 185 pod.Spec.Containers[0].Env = []v1.EnvVar{{Name: fmt.Sprintf("SERVE_PORT_%d", port), Value: "foo"}} 186 pod.Spec.RestartPolicy = v1.RestartPolicyNever 187 return pod 188 }