...

Source file src/k8s.io/kubernetes/test/integration/apiserver/print_test.go

Documentation: k8s.io/kubernetes/test/integration/apiserver

     1  /*
     2  Copyright 2018 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 apiserver
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"os"
    24  	"reflect"
    25  	"strings"
    26  	"testing"
    27  	"time"
    28  
    29  	"k8s.io/apimachinery/pkg/api/meta"
    30  	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	"k8s.io/apimachinery/pkg/runtime/schema"
    33  	"k8s.io/apimachinery/pkg/util/sets"
    34  	"k8s.io/cli-runtime/pkg/genericclioptions"
    35  	diskcached "k8s.io/client-go/discovery/cached/disk"
    36  	"k8s.io/client-go/tools/clientcmd"
    37  	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
    38  	"k8s.io/kubectl/pkg/cmd/util"
    39  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    40  	"k8s.io/kubernetes/pkg/printers"
    41  	printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
    42  	"k8s.io/kubernetes/test/integration/framework"
    43  )
    44  
    45  var kindAllowList = sets.NewString(
    46  	// k8s.io/api/core
    47  	"APIGroup",
    48  	"APIVersions",
    49  	"Binding",
    50  	"DeleteOptions",
    51  	"EphemeralContainers",
    52  	"ExportOptions",
    53  	"GetOptions",
    54  	"ListOptions",
    55  	"CreateOptions",
    56  	"UpdateOptions",
    57  	"PatchOptions",
    58  	"NodeProxyOptions",
    59  	"PodAttachOptions",
    60  	"PodExecOptions",
    61  	"PodPortForwardOptions",
    62  	"PodLogOptions",
    63  	"PodProxyOptions",
    64  	"PodStatusResult",
    65  	"RangeAllocation",
    66  	"ServiceProxyOptions",
    67  	"SerializedReference",
    68  	// --
    69  
    70  	// k8s.io/api/admission
    71  	"AdmissionReview",
    72  	// --
    73  
    74  	// k8s.io/api/authentication
    75  	"TokenRequest",
    76  	"TokenReview",
    77  	// --
    78  
    79  	// k8s.io/api/authorization
    80  	"LocalSubjectAccessReview",
    81  	"SelfSubjectAccessReview",
    82  	"SelfSubjectRulesReview",
    83  	"SubjectAccessReview",
    84  	// --
    85  
    86  	// k8s.io/api/autoscaling
    87  	"Scale",
    88  	// --
    89  
    90  	// k8s.io/api/apps
    91  	"DeploymentRollback",
    92  	// --
    93  
    94  	// k8s.io/api/batch
    95  	"JobTemplate",
    96  	// --
    97  
    98  	// k8s.io/api/imagepolicy
    99  	"ImageReview",
   100  	// --
   101  
   102  	// k8s.io/api/policy
   103  	"Eviction",
   104  	// --
   105  
   106  	// k8s.io/apimachinery/pkg/apis/meta
   107  	"WatchEvent",
   108  	"Status",
   109  	// --
   110  )
   111  
   112  // TODO (soltysh): this list has to go down to 0!
   113  var missingHanlders = sets.NewString(
   114  	"ClusterRole",
   115  	"LimitRange",
   116  	"ResourceQuota",
   117  	"Role",
   118  	"PriorityClass",
   119  	"AuditSink",
   120  )
   121  
   122  func TestServerSidePrint(t *testing.T) {
   123  	_, clientSet, kubeConfig, tearDownFn := setupWithResources(t,
   124  		// additional groupversions needed for the test to run
   125  		[]schema.GroupVersion{
   126  			{Group: "discovery.k8s.io", Version: "v1"},
   127  			{Group: "discovery.k8s.io", Version: "v1beta1"},
   128  			{Group: "rbac.authorization.k8s.io", Version: "v1alpha1"},
   129  			{Group: "scheduling.k8s.io", Version: "v1"},
   130  			{Group: "storage.k8s.io", Version: "v1alpha1"},
   131  			{Group: "storage.k8s.io", Version: "v1beta1"},
   132  			{Group: "extensions", Version: "v1beta1"},
   133  			{Group: "node.k8s.io", Version: "v1"},
   134  			{Group: "node.k8s.io", Version: "v1alpha1"},
   135  			{Group: "node.k8s.io", Version: "v1beta1"},
   136  			{Group: "flowcontrol.apiserver.k8s.io", Version: "v1alpha1"},
   137  			{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta1"},
   138  			{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta2"},
   139  			{Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta3"},
   140  			{Group: "flowcontrol.apiserver.k8s.io", Version: "v1"},
   141  			{Group: "internal.apiserver.k8s.io", Version: "v1alpha1"},
   142  		},
   143  		[]schema.GroupVersionResource{},
   144  	)
   145  	defer tearDownFn()
   146  
   147  	ns := framework.CreateNamespaceOrDie(clientSet, "server-print", t)
   148  	defer framework.DeleteNamespaceOrDie(clientSet, ns, t)
   149  
   150  	tableParam := fmt.Sprintf("application/json;as=Table;g=%s;v=%s, application/json", metav1beta1.GroupName, metav1beta1.SchemeGroupVersion.Version)
   151  	printer := newFakePrinter(printersinternal.AddHandlers)
   152  
   153  	configFlags := genericclioptions.NewTestConfigFlags().
   154  		WithClientConfig(clientcmd.NewDefaultClientConfig(*createKubeConfig(kubeConfig.Host), &clientcmd.ConfigOverrides{}))
   155  
   156  	restConfig, err := configFlags.ToRESTConfig()
   157  	if err != nil {
   158  		t.Errorf("unexpected error: %v", err)
   159  	}
   160  
   161  	cacheDir, err := os.MkdirTemp(os.TempDir(), "test-integration-apiserver-print")
   162  	if err != nil {
   163  		t.Errorf("unexpected error: %v", err)
   164  	}
   165  	defer func() {
   166  		os.Remove(cacheDir)
   167  	}()
   168  
   169  	cachedClient, err := diskcached.NewCachedDiscoveryClientForConfig(restConfig, cacheDir, "", time.Duration(10*time.Minute))
   170  	if err != nil {
   171  		t.Errorf("unexpected error: %v", err)
   172  	}
   173  
   174  	configFlags.WithDiscoveryClient(cachedClient)
   175  
   176  	factory := util.NewFactory(configFlags)
   177  	mapper, err := factory.ToRESTMapper()
   178  	if err != nil {
   179  		t.Errorf("unexpected error getting mapper: %v", err)
   180  		return
   181  	}
   182  	for gvk, apiType := range legacyscheme.Scheme.AllKnownTypes() {
   183  		// we do not care about internal objects or lists // TODO make sure this is always true
   184  		if gvk.Version == runtime.APIVersionInternal || strings.HasSuffix(apiType.Name(), "List") {
   185  			continue
   186  		}
   187  		if kindAllowList.Has(gvk.Kind) || missingHanlders.Has(gvk.Kind) {
   188  			continue
   189  		}
   190  
   191  		t.Logf("Checking %s", gvk)
   192  		// read table definition as returned by the server
   193  		mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
   194  		if err != nil {
   195  			// if we have no mapping, we aren't serving it and we don't need to check its printer.
   196  			t.Logf("unexpected error getting mapping for GVK %s: %v", gvk, err)
   197  			continue
   198  		}
   199  		client, err := factory.ClientForMapping(mapping)
   200  		if err != nil {
   201  			t.Errorf("unexpected error getting client for GVK %s: %v", gvk, err)
   202  			continue
   203  		}
   204  		req := client.Get()
   205  		if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
   206  			req = req.Namespace(ns.Name)
   207  		}
   208  		body, err := req.Resource(mapping.Resource.Resource).SetHeader("Accept", tableParam).Do(context.TODO()).Raw()
   209  		if err != nil {
   210  			t.Errorf("unexpected error getting %s: %v", gvk, err)
   211  			continue
   212  		}
   213  		actual, err := decodeIntoTable(body)
   214  		if err != nil {
   215  			t.Errorf("unexpected error decoding %s: %v", gvk, err)
   216  			continue
   217  		}
   218  
   219  		// get table definition used in printers
   220  		obj, err := legacyscheme.Scheme.New(gvk)
   221  		if err != nil {
   222  			t.Errorf("unexpected error creating %s: %v", gvk, err)
   223  			continue
   224  		}
   225  		intGV := gvk.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
   226  		intObj, err := legacyscheme.Scheme.ConvertToVersion(obj, intGV)
   227  		if err != nil {
   228  			t.Errorf("unexpected error converting %s to internal: %v", gvk, err)
   229  			continue
   230  		}
   231  		expectedColumnDefinitions, ok := printer.handlers[reflect.TypeOf(intObj)]
   232  		if !ok {
   233  			t.Errorf("missing handler for type %v", gvk)
   234  			continue
   235  		}
   236  
   237  		for _, e := range expectedColumnDefinitions {
   238  			for _, a := range actual.ColumnDefinitions {
   239  				if a.Name == e.Name && !reflect.DeepEqual(a, e) {
   240  					t.Errorf("unexpected difference in column definition %s for %s:\nexpected:\n%#v\nactual:\n%#v\n", e.Name, gvk, e, a)
   241  				}
   242  			}
   243  		}
   244  	}
   245  }
   246  
   247  type fakePrinter struct {
   248  	handlers map[reflect.Type][]metav1beta1.TableColumnDefinition
   249  }
   250  
   251  var _ printers.PrintHandler = &fakePrinter{}
   252  
   253  func (f *fakePrinter) Handler(columns, columnsWithWide []string, printFunc interface{}) error {
   254  	return nil
   255  }
   256  
   257  func (f *fakePrinter) TableHandler(columns []metav1beta1.TableColumnDefinition, printFunc interface{}) error {
   258  	printFuncValue := reflect.ValueOf(printFunc)
   259  	objType := printFuncValue.Type().In(0)
   260  	f.handlers[objType] = columns
   261  	return nil
   262  }
   263  
   264  func (f *fakePrinter) DefaultTableHandler(columns []metav1beta1.TableColumnDefinition, printFunc interface{}) error {
   265  	return nil
   266  }
   267  
   268  func newFakePrinter(fns ...func(printers.PrintHandler)) *fakePrinter {
   269  	handlers := make(map[reflect.Type][]metav1beta1.TableColumnDefinition, len(fns))
   270  	p := &fakePrinter{handlers: handlers}
   271  	for _, fn := range fns {
   272  		fn(p)
   273  	}
   274  	return p
   275  }
   276  
   277  func decodeIntoTable(body []byte) (*metav1beta1.Table, error) {
   278  	table := &metav1beta1.Table{}
   279  	err := json.Unmarshal(body, table)
   280  	if err != nil {
   281  		return nil, err
   282  	}
   283  	return table, nil
   284  }
   285  
   286  func createKubeConfig(url string) *clientcmdapi.Config {
   287  	clusterNick := "cluster"
   288  	userNick := "user"
   289  	contextNick := "context"
   290  
   291  	config := clientcmdapi.NewConfig()
   292  
   293  	cluster := clientcmdapi.NewCluster()
   294  	cluster.Server = url
   295  	cluster.InsecureSkipTLSVerify = true
   296  	config.Clusters[clusterNick] = cluster
   297  
   298  	context := clientcmdapi.NewContext()
   299  	context.Cluster = clusterNick
   300  	context.AuthInfo = userNick
   301  	config.Contexts[contextNick] = context
   302  	config.CurrentContext = contextNick
   303  
   304  	return config
   305  }
   306  

View as plain text