mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 05:27:21 +00:00
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:
commit
254f657df2
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user