Merge pull request #83748 from robscott/kubectl-e2e
Adding e2e test to ensure kubectl get output is using custom columns when desired
This commit is contained in:
		@@ -19,15 +19,19 @@ go_library(
 | 
				
			|||||||
        "//staging/src/k8s.io/api/rbac/v1:go_default_library",
 | 
					        "//staging/src/k8s.io/api/rbac/v1:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1:go_default_library",
 | 
					        "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
 | 
					        "//staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
 | 
					        "//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/client-go/dynamic:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/kubectl/pkg/polymorphichelpers:go_default_library",
 | 
					        "//staging/src/k8s.io/kubectl/pkg/polymorphichelpers:go_default_library",
 | 
				
			||||||
        "//test/e2e/common:go_default_library",
 | 
					        "//test/e2e/common:go_default_library",
 | 
				
			||||||
@@ -39,6 +43,7 @@ go_library(
 | 
				
			|||||||
        "//test/e2e/framework/service:go_default_library",
 | 
					        "//test/e2e/framework/service:go_default_library",
 | 
				
			||||||
        "//test/e2e/framework/testfiles:go_default_library",
 | 
					        "//test/e2e/framework/testfiles:go_default_library",
 | 
				
			||||||
        "//test/e2e/scheduling:go_default_library",
 | 
					        "//test/e2e/scheduling:go_default_library",
 | 
				
			||||||
 | 
					        "//test/integration/etcd:go_default_library",
 | 
				
			||||||
        "//test/utils:go_default_library",
 | 
					        "//test/utils:go_default_library",
 | 
				
			||||||
        "//test/utils/crd:go_default_library",
 | 
					        "//test/utils/crd:go_default_library",
 | 
				
			||||||
        "//test/utils/image:go_default_library",
 | 
					        "//test/utils/image:go_default_library",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,15 +46,19 @@ import (
 | 
				
			|||||||
	rbacv1 "k8s.io/api/rbac/v1"
 | 
						rbacv1 "k8s.io/api/rbac/v1"
 | 
				
			||||||
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 | 
						apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 | 
				
			||||||
	apierrs "k8s.io/apimachinery/pkg/api/errors"
 | 
						apierrs "k8s.io/apimachinery/pkg/api/errors"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/api/meta"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
						"k8s.io/apimachinery/pkg/api/resource"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/labels"
 | 
						"k8s.io/apimachinery/pkg/labels"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
						"k8s.io/apimachinery/pkg/runtime/schema"
 | 
				
			||||||
	utilnet "k8s.io/apimachinery/pkg/util/net"
 | 
						utilnet "k8s.io/apimachinery/pkg/util/net"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/util/rand"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/uuid"
 | 
						"k8s.io/apimachinery/pkg/util/uuid"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/wait"
 | 
						"k8s.io/apimachinery/pkg/util/wait"
 | 
				
			||||||
	"k8s.io/apiserver/pkg/authentication/serviceaccount"
 | 
						"k8s.io/apiserver/pkg/authentication/serviceaccount"
 | 
				
			||||||
	genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
 | 
						genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
 | 
				
			||||||
 | 
						"k8s.io/client-go/dynamic"
 | 
				
			||||||
	clientset "k8s.io/client-go/kubernetes"
 | 
						clientset "k8s.io/client-go/kubernetes"
 | 
				
			||||||
	"k8s.io/kubectl/pkg/polymorphichelpers"
 | 
						"k8s.io/kubectl/pkg/polymorphichelpers"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/controller"
 | 
						"k8s.io/kubernetes/pkg/controller"
 | 
				
			||||||
@@ -67,6 +71,7 @@ import (
 | 
				
			|||||||
	e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
 | 
						e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
 | 
				
			||||||
	"k8s.io/kubernetes/test/e2e/framework/testfiles"
 | 
						"k8s.io/kubernetes/test/e2e/framework/testfiles"
 | 
				
			||||||
	"k8s.io/kubernetes/test/e2e/scheduling"
 | 
						"k8s.io/kubernetes/test/e2e/scheduling"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/test/integration/etcd"
 | 
				
			||||||
	testutils "k8s.io/kubernetes/test/utils"
 | 
						testutils "k8s.io/kubernetes/test/utils"
 | 
				
			||||||
	"k8s.io/kubernetes/test/utils/crd"
 | 
						"k8s.io/kubernetes/test/utils/crd"
 | 
				
			||||||
	imageutils "k8s.io/kubernetes/test/utils/image"
 | 
						imageutils "k8s.io/kubernetes/test/utils/image"
 | 
				
			||||||
@@ -410,6 +415,82 @@ var _ = SIGDescribe("Kubectl client", func() {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ginkgo.Describe("kubectl get output", func() {
 | 
				
			||||||
 | 
							ginkgo.It("should contain custom columns for each resource", func() {
 | 
				
			||||||
 | 
								randString := rand.String(10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ignoredResources := map[string]bool{
 | 
				
			||||||
 | 
									// ignored for intentionally using standard fields.
 | 
				
			||||||
 | 
									// This assumption is based on a lack of TableColumnDefinition
 | 
				
			||||||
 | 
									// in pkg/printers/internalversion/printers.go
 | 
				
			||||||
 | 
									"ClusterRole": true,
 | 
				
			||||||
 | 
									"LimitRange":  true,
 | 
				
			||||||
 | 
									"PodPreset":   true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// ignored temporarily while waiting for bug fix.
 | 
				
			||||||
 | 
									"ComponentStatus":    true,
 | 
				
			||||||
 | 
									"ClusterRoleBinding": true,
 | 
				
			||||||
 | 
									"ResourceQuota":      true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// ignored because no test data exists.
 | 
				
			||||||
 | 
									// Do not add anything to this list, instead add fixtures in
 | 
				
			||||||
 | 
									// the test/integration/etcd package.
 | 
				
			||||||
 | 
									"BackendConfig":         true,
 | 
				
			||||||
 | 
									"NodeMetrics":           true,
 | 
				
			||||||
 | 
									"PodMetrics":            true,
 | 
				
			||||||
 | 
									"ScalingPolicy":         true,
 | 
				
			||||||
 | 
									"VolumeSnapshotClass":   true,
 | 
				
			||||||
 | 
									"VolumeSnapshotContent": true,
 | 
				
			||||||
 | 
									"VolumeSnapshot":        true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// A CRD created by other e2e tests without any test data.
 | 
				
			||||||
 | 
									"Noxu": true,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								apiGroups, err := c.Discovery().ServerPreferredResources()
 | 
				
			||||||
 | 
								framework.ExpectNoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								testableResources := etcd.GetEtcdStorageDataForNamespace(f.Namespace.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for _, group := range apiGroups {
 | 
				
			||||||
 | 
									for _, resource := range group.APIResources {
 | 
				
			||||||
 | 
										if !verbsContain(resource.Verbs, "get") || ignoredResources[resource.Kind] || strings.HasPrefix(resource.Name, "e2e-test") {
 | 
				
			||||||
 | 
											continue
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// compute gvr
 | 
				
			||||||
 | 
										gv, err := schema.ParseGroupVersion(group.GroupVersion)
 | 
				
			||||||
 | 
										framework.ExpectNoError(err)
 | 
				
			||||||
 | 
										gvr := gv.WithResource(resource.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// assert test data exists
 | 
				
			||||||
 | 
										testData := testableResources[gvr]
 | 
				
			||||||
 | 
										gomega.ExpectWithOffset(1, testData).ToNot(gomega.BeZero(), "No test data available for %s", gvr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// create test resource
 | 
				
			||||||
 | 
										mapping := &meta.RESTMapping{
 | 
				
			||||||
 | 
											Resource:         gvr,
 | 
				
			||||||
 | 
											GroupVersionKind: gv.WithKind(resource.Kind),
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if resource.Namespaced {
 | 
				
			||||||
 | 
											mapping.Scope = meta.RESTScopeNamespace
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											mapping.Scope = meta.RESTScopeRoot
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										client, obj, err := etcd.JSONToUnstructured(testData.Stub, f.Namespace.Name, mapping, f.DynamicClient)
 | 
				
			||||||
 | 
										framework.ExpectNoError(err)
 | 
				
			||||||
 | 
										if resource.Kind != "APIService" && resource.Kind != "CustomResourceDefinition" {
 | 
				
			||||||
 | 
											obj.SetName(obj.GetName() + randString)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										createObjValidateOutputAndCleanup(client, obj, resource)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ginkgo.Describe("Simple pod", func() {
 | 
						ginkgo.Describe("Simple pod", func() {
 | 
				
			||||||
		var podYaml string
 | 
							var podYaml string
 | 
				
			||||||
		ginkgo.BeforeEach(func() {
 | 
							ginkgo.BeforeEach(func() {
 | 
				
			||||||
@@ -2482,3 +2563,43 @@ waitLoop:
 | 
				
			|||||||
	// Reaching here means that one of more checks failed multiple times.  Assuming its not a race condition, something is broken.
 | 
						// Reaching here means that one of more checks failed multiple times.  Assuming its not a race condition, something is broken.
 | 
				
			||||||
	framework.Failf("Timed out after %v seconds waiting for %s pods to reach valid state", framework.PodStartTimeout.Seconds(), testname)
 | 
						framework.Failf("Timed out after %v seconds waiting for %s pods to reach valid state", framework.PodStartTimeout.Seconds(), testname)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// verbsContain returns true if the provided list of verbs contain the provided
 | 
				
			||||||
 | 
					// verb string.
 | 
				
			||||||
 | 
					func verbsContain(verbs metav1.Verbs, str string) bool {
 | 
				
			||||||
 | 
						for _, v := range verbs {
 | 
				
			||||||
 | 
							if v == str {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// deleteObj deletes an Object with the provided client and name.
 | 
				
			||||||
 | 
					func deleteObj(client dynamic.ResourceInterface, name string) {
 | 
				
			||||||
 | 
						err := client.Delete(name, &metav1.DeleteOptions{})
 | 
				
			||||||
 | 
						framework.ExpectNoError(err)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// createObjValidateOutputAndCleanup creates an object using the provided client
 | 
				
			||||||
 | 
					// and then verifies that the kubectl get output provides custom columns. Once
 | 
				
			||||||
 | 
					// the test has completed, it deletes the object.
 | 
				
			||||||
 | 
					func createObjValidateOutputAndCleanup(client dynamic.ResourceInterface, obj *unstructured.Unstructured, resource metav1.APIResource) {
 | 
				
			||||||
 | 
						_, err := client.Create(obj, metav1.CreateOptions{})
 | 
				
			||||||
 | 
						framework.ExpectNoError(err)
 | 
				
			||||||
 | 
						defer deleteObj(client, obj.GetName())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// get test resource
 | 
				
			||||||
 | 
						output := framework.RunKubectlOrDie("get", resource.Name, "--all-namespaces")
 | 
				
			||||||
 | 
						if output == "" {
 | 
				
			||||||
 | 
							framework.Failf("No stdout from kubectl get for %s (likely need to define test resources)", resource.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						splitOutput := strings.SplitN(output, "\n", 2)
 | 
				
			||||||
 | 
						fields := strings.Fields(splitOutput[0])
 | 
				
			||||||
 | 
						if resource.Namespaced {
 | 
				
			||||||
 | 
							framework.ExpectNotEqual(fields, []string{"NAMESPACE", "NAME", "CREATED", "AT"}, fmt.Sprintf("expected non-default fields for namespaced resource: %s", resource.Name))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							framework.ExpectNotEqual(fields, []string{"NAME", "AGE"}, fmt.Sprintf("expected non-default fields for resource: %s", resource.Name))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user