print_test.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package apiserver
  14. import (
  15. "encoding/json"
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "reflect"
  20. "strings"
  21. "testing"
  22. "time"
  23. appsv1beta1 "k8s.io/api/apps/v1beta1"
  24. appsv1beta2 "k8s.io/api/apps/v1beta2"
  25. auditregv1alpha1 "k8s.io/api/auditregistration/v1alpha1"
  26. batchv2alpha1 "k8s.io/api/batch/v2alpha1"
  27. extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
  28. nodev1alpha1 "k8s.io/api/node/v1alpha1"
  29. rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
  30. schedulerapi "k8s.io/api/scheduling/v1"
  31. settingsv1alpha1 "k8s.io/api/settings/v1alpha1"
  32. storagev1alpha1 "k8s.io/api/storage/v1alpha1"
  33. "k8s.io/apimachinery/pkg/api/meta"
  34. metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
  35. "k8s.io/apimachinery/pkg/runtime"
  36. "k8s.io/apimachinery/pkg/runtime/schema"
  37. "k8s.io/cli-runtime/pkg/genericclioptions"
  38. diskcached "k8s.io/client-go/discovery/cached/disk"
  39. "k8s.io/client-go/tools/clientcmd"
  40. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  41. "k8s.io/gengo/examples/set-gen/sets"
  42. "k8s.io/kubernetes/pkg/api/legacyscheme"
  43. "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  44. "k8s.io/kubernetes/pkg/printers"
  45. printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
  46. "k8s.io/kubernetes/test/integration/framework"
  47. )
  48. var kindWhiteList = sets.NewString(
  49. // k8s.io/api/core
  50. "APIGroup",
  51. "APIVersions",
  52. "Binding",
  53. "DeleteOptions",
  54. "ExportOptions",
  55. "GetOptions",
  56. "ListOptions",
  57. "CreateOptions",
  58. "UpdateOptions",
  59. "PatchOptions",
  60. "NodeProxyOptions",
  61. "PodAttachOptions",
  62. "PodExecOptions",
  63. "PodPortForwardOptions",
  64. "PodLogOptions",
  65. "PodProxyOptions",
  66. "PodStatusResult",
  67. "RangeAllocation",
  68. "ServiceProxyOptions",
  69. "SerializedReference",
  70. // --
  71. // k8s.io/api/admission
  72. "AdmissionReview",
  73. // --
  74. // k8s.io/api/authentication
  75. "TokenRequest",
  76. "TokenReview",
  77. // --
  78. // k8s.io/api/authorization
  79. "LocalSubjectAccessReview",
  80. "SelfSubjectAccessReview",
  81. "SelfSubjectRulesReview",
  82. "SubjectAccessReview",
  83. // --
  84. // k8s.io/api/autoscaling
  85. "Scale",
  86. // --
  87. // k8s.io/api/apps
  88. "DeploymentRollback",
  89. // --
  90. // k8s.io/api/batch
  91. "JobTemplate",
  92. // --
  93. // k8s.io/api/extensions
  94. "ReplicationControllerDummy",
  95. // --
  96. // k8s.io/api/imagepolicy
  97. "ImageReview",
  98. // --
  99. // k8s.io/api/policy
  100. "Eviction",
  101. // --
  102. // k8s.io/apimachinery/pkg/apis/meta
  103. "WatchEvent",
  104. "Status",
  105. // --
  106. )
  107. // TODO (soltysh): this list has to go down to 0!
  108. var missingHanlders = sets.NewString(
  109. "ClusterRole",
  110. "LimitRange",
  111. "MutatingWebhookConfiguration",
  112. "ResourceQuota",
  113. "Role",
  114. "ValidatingWebhookConfiguration",
  115. "VolumeAttachment",
  116. "PriorityClass",
  117. "PodPreset",
  118. "AuditSink",
  119. "CSINode",
  120. "CSIDriver",
  121. )
  122. func TestServerSidePrint(t *testing.T) {
  123. s, _, closeFn := setupWithResources(t,
  124. // additional groupversions needed for the test to run
  125. []schema.GroupVersion{
  126. auditregv1alpha1.SchemeGroupVersion,
  127. batchv2alpha1.SchemeGroupVersion,
  128. rbacv1alpha1.SchemeGroupVersion,
  129. settingsv1alpha1.SchemeGroupVersion,
  130. schedulerapi.SchemeGroupVersion,
  131. storagev1alpha1.SchemeGroupVersion,
  132. appsv1beta1.SchemeGroupVersion,
  133. appsv1beta2.SchemeGroupVersion,
  134. extensionsv1beta1.SchemeGroupVersion,
  135. nodev1alpha1.SchemeGroupVersion,
  136. },
  137. []schema.GroupVersionResource{
  138. extensionsv1beta1.SchemeGroupVersion.WithResource("daemonsets"),
  139. extensionsv1beta1.SchemeGroupVersion.WithResource("deployments"),
  140. extensionsv1beta1.SchemeGroupVersion.WithResource("networkpolicies"),
  141. extensionsv1beta1.SchemeGroupVersion.WithResource("podsecuritypolicies"),
  142. extensionsv1beta1.SchemeGroupVersion.WithResource("replicasets"),
  143. },
  144. )
  145. defer closeFn()
  146. ns := framework.CreateTestingNamespace("server-print", s, t)
  147. defer framework.DeleteTestingNamespace(ns, s, t)
  148. tableParam := fmt.Sprintf("application/json;as=Table;g=%s;v=%s, application/json", metav1beta1.GroupName, metav1beta1.SchemeGroupVersion.Version)
  149. printer := newFakePrinter(printersinternal.AddHandlers)
  150. configFlags := genericclioptions.NewTestConfigFlags().
  151. WithClientConfig(clientcmd.NewDefaultClientConfig(*createKubeConfig(s.URL), &clientcmd.ConfigOverrides{}))
  152. restConfig, err := configFlags.ToRESTConfig()
  153. if err != nil {
  154. t.Errorf("unexpected error: %v", err)
  155. }
  156. cacheDir, err := ioutil.TempDir(os.TempDir(), "test-integration-apiserver-print")
  157. if err != nil {
  158. t.Errorf("unexpected error: %v", err)
  159. }
  160. defer func() {
  161. os.Remove(cacheDir)
  162. }()
  163. cachedClient, err := diskcached.NewCachedDiscoveryClientForConfig(restConfig, cacheDir, "", time.Duration(10*time.Minute))
  164. if err != nil {
  165. t.Errorf("unexpected error: %v", err)
  166. }
  167. configFlags.WithDiscoveryClient(cachedClient)
  168. factory := util.NewFactory(configFlags)
  169. mapper, err := factory.ToRESTMapper()
  170. if err != nil {
  171. t.Errorf("unexpected error getting mapper: %v", err)
  172. return
  173. }
  174. for gvk, apiType := range legacyscheme.Scheme.AllKnownTypes() {
  175. // we do not care about internal objects or lists // TODO make sure this is always true
  176. if gvk.Version == runtime.APIVersionInternal || strings.HasSuffix(apiType.Name(), "List") {
  177. continue
  178. }
  179. if kindWhiteList.Has(gvk.Kind) || missingHanlders.Has(gvk.Kind) {
  180. continue
  181. }
  182. t.Logf("Checking %s", gvk)
  183. // read table definition as returned by the server
  184. mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
  185. if err != nil {
  186. t.Errorf("unexpected error getting mapping for GVK %s: %v", gvk, err)
  187. continue
  188. }
  189. client, err := factory.ClientForMapping(mapping)
  190. if err != nil {
  191. t.Errorf("unexpected error getting client for GVK %s: %v", gvk, err)
  192. continue
  193. }
  194. req := client.Get()
  195. if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
  196. req = req.Namespace(ns.Name)
  197. }
  198. body, err := req.Resource(mapping.Resource.Resource).SetHeader("Accept", tableParam).Do().Raw()
  199. if err != nil {
  200. t.Errorf("unexpected error getting %s: %v", gvk, err)
  201. continue
  202. }
  203. actual, err := decodeIntoTable(body)
  204. if err != nil {
  205. t.Errorf("unexpected error decoding %s: %v", gvk, err)
  206. continue
  207. }
  208. // get table definition used in printers
  209. obj, err := legacyscheme.Scheme.New(gvk)
  210. if err != nil {
  211. t.Errorf("unexpected error creating %s: %v", gvk, err)
  212. continue
  213. }
  214. intGV := gvk.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
  215. intObj, err := legacyscheme.Scheme.ConvertToVersion(obj, intGV)
  216. if err != nil {
  217. t.Errorf("unexpected error converting %s to internal: %v", gvk, err)
  218. continue
  219. }
  220. expectedColumnDefinitions, ok := printer.handlers[reflect.TypeOf(intObj)]
  221. if !ok {
  222. t.Errorf("missing handler for type %v", gvk)
  223. continue
  224. }
  225. for _, e := range expectedColumnDefinitions {
  226. for _, a := range actual.ColumnDefinitions {
  227. if a.Name == e.Name && !reflect.DeepEqual(a, e) {
  228. t.Errorf("unexpected difference in column definition %s for %s:\nexpected:\n%#v\nactual:\n%#v\n", e.Name, gvk, e, a)
  229. }
  230. }
  231. }
  232. }
  233. }
  234. type fakePrinter struct {
  235. handlers map[reflect.Type][]metav1beta1.TableColumnDefinition
  236. }
  237. var _ printers.PrintHandler = &fakePrinter{}
  238. func (f *fakePrinter) Handler(columns, columnsWithWide []string, printFunc interface{}) error {
  239. return nil
  240. }
  241. func (f *fakePrinter) TableHandler(columns []metav1beta1.TableColumnDefinition, printFunc interface{}) error {
  242. printFuncValue := reflect.ValueOf(printFunc)
  243. objType := printFuncValue.Type().In(0)
  244. f.handlers[objType] = columns
  245. return nil
  246. }
  247. func (f *fakePrinter) DefaultTableHandler(columns []metav1beta1.TableColumnDefinition, printFunc interface{}) error {
  248. return nil
  249. }
  250. func newFakePrinter(fns ...func(printers.PrintHandler)) *fakePrinter {
  251. handlers := make(map[reflect.Type][]metav1beta1.TableColumnDefinition, len(fns))
  252. p := &fakePrinter{handlers: handlers}
  253. for _, fn := range fns {
  254. fn(p)
  255. }
  256. return p
  257. }
  258. func decodeIntoTable(body []byte) (*metav1beta1.Table, error) {
  259. table := &metav1beta1.Table{}
  260. err := json.Unmarshal(body, table)
  261. if err != nil {
  262. return nil, err
  263. }
  264. return table, nil
  265. }
  266. func createKubeConfig(url string) *clientcmdapi.Config {
  267. clusterNick := "cluster"
  268. userNick := "user"
  269. contextNick := "context"
  270. config := clientcmdapi.NewConfig()
  271. cluster := clientcmdapi.NewCluster()
  272. cluster.Server = url
  273. cluster.InsecureSkipTLSVerify = true
  274. config.Clusters[clusterNick] = cluster
  275. context := clientcmdapi.NewContext()
  276. context.Cluster = clusterNick
  277. context.AuthInfo = userNick
  278. config.Contexts[contextNick] = context
  279. config.CurrentContext = contextNick
  280. return config
  281. }