1
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
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 }
189
View as plain text