add explain tests for openapiv3
Kubernetes-commit: 9597abd089cd2f7c0e042ed42a3b4a803225d562
This commit is contained in:
parent
dd44b643a3
commit
62c7fa911e
|
@ -14,24 +14,31 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package explain
|
||||
package explain_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
sptest "k8s.io/apimachinery/pkg/util/strategicpatch/testing"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/client-go/discovery"
|
||||
openapiclient "k8s.io/client-go/openapi"
|
||||
"k8s.io/client-go/rest"
|
||||
clienttestutil "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubectl/pkg/cmd/explain"
|
||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/util/openapi"
|
||||
)
|
||||
|
||||
var (
|
||||
fakeSchema = sptest.Fake{Path: filepath.Join("..", "..", "..", "testdata", "openapi", "swagger.json")}
|
||||
testDataPath = filepath.Join("..", "..", "..", "testdata")
|
||||
fakeSchema = sptest.Fake{Path: filepath.Join(testDataPath, "openapi", "swagger.json")}
|
||||
FakeOpenAPISchema = testOpenAPISchema{
|
||||
OpenAPISchemaFn: func() (openapi.Resources, error) {
|
||||
s, err := fakeSchema.OpenAPISchema()
|
||||
|
@ -51,8 +58,8 @@ func TestExplainInvalidArgs(t *testing.T) {
|
|||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
|
||||
opts := NewExplainOptions("kubectl", genericclioptions.NewTestIOStreamsDiscard())
|
||||
cmd := NewCmdExplain("kubectl", tf, genericclioptions.NewTestIOStreamsDiscard())
|
||||
opts := explain.NewExplainOptions("kubectl", genericclioptions.NewTestIOStreamsDiscard())
|
||||
cmd := explain.NewCmdExplain("kubectl", tf, genericclioptions.NewTestIOStreamsDiscard())
|
||||
err := opts.Complete(tf, cmd, []string{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
|
@ -78,8 +85,8 @@ func TestExplainNotExistResource(t *testing.T) {
|
|||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
|
||||
opts := NewExplainOptions("kubectl", genericclioptions.NewTestIOStreamsDiscard())
|
||||
cmd := NewCmdExplain("kubectl", tf, genericclioptions.NewTestIOStreamsDiscard())
|
||||
opts := explain.NewExplainOptions("kubectl", genericclioptions.NewTestIOStreamsDiscard())
|
||||
cmd := explain.NewCmdExplain("kubectl", tf, genericclioptions.NewTestIOStreamsDiscard())
|
||||
err := opts.Complete(tf, cmd, []string{"foo"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
|
@ -96,30 +103,106 @@ func TestExplainNotExistResource(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestExplainNotExistVersion(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
type explainTestCase struct {
|
||||
Name string
|
||||
Args []string
|
||||
Flags map[string]string
|
||||
ExpectPattern []string
|
||||
ExpectErrorPattern string
|
||||
|
||||
opts := NewExplainOptions("kubectl", genericclioptions.NewTestIOStreamsDiscard())
|
||||
cmd := NewCmdExplain("kubectl", tf, genericclioptions.NewTestIOStreamsDiscard())
|
||||
err := opts.Complete(tf, cmd, []string{"pods"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
opts.APIVersion = "v99"
|
||||
|
||||
err = opts.Validate()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
err = opts.Run()
|
||||
if err.Error() != "couldn't find resource for \"/v99, Kind=Pod\"" {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
// Custom OpenAPI V3 client to use for the test. If nil, a default one will
|
||||
// be provided
|
||||
OpenAPIV3SchemaFn func() (openapiclient.Client, error)
|
||||
}
|
||||
|
||||
func TestExplain(t *testing.T) {
|
||||
var explainV2Cases = []explainTestCase{
|
||||
{
|
||||
Name: "Basic",
|
||||
Args: []string{"pods"},
|
||||
ExpectPattern: []string{`\s*KIND:[\t ]*Pod\s*`},
|
||||
},
|
||||
{
|
||||
Name: "Recursive",
|
||||
Args: []string{"pods"},
|
||||
Flags: map[string]string{"recursive": "true"},
|
||||
ExpectPattern: []string{`\s*KIND:[\t ]*Pod\s*`},
|
||||
},
|
||||
{
|
||||
Name: "DefaultAPIVersion",
|
||||
Args: []string{"horizontalpodautoscalers"},
|
||||
Flags: map[string]string{"api-version": "autoscaling/v1"},
|
||||
ExpectPattern: []string{`\s*VERSION:[\t ]*(v1|autoscaling/v1)\s*`},
|
||||
},
|
||||
{
|
||||
Name: "NonExistingAPIVersion",
|
||||
Args: []string{"pods"},
|
||||
Flags: map[string]string{"api-version": "v99"},
|
||||
ExpectErrorPattern: `couldn't find resource for \"/v99, (Kind=Pod|Resource=pods)\"`,
|
||||
},
|
||||
{
|
||||
Name: "NonExistingResource",
|
||||
Args: []string{"foo"},
|
||||
ExpectErrorPattern: `the server doesn't have a resource type "foo"`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestExplainOpenAPIV2(t *testing.T) {
|
||||
runExplainTestCases(t, explainV2Cases)
|
||||
}
|
||||
|
||||
func TestExplainOpenAPIV3(t *testing.T) {
|
||||
|
||||
fallbackV3SchemaFn := func() (openapiclient.Client, error) {
|
||||
fakeDiscoveryClient := discovery.NewDiscoveryClientForConfigOrDie(&rest.Config{Host: "https://not.a.real.site:65543/"})
|
||||
return fakeDiscoveryClient.OpenAPIV3(), nil
|
||||
}
|
||||
// Returns a client that causes fallback to v2 implementation
|
||||
cases := []explainTestCase{
|
||||
{
|
||||
// No --output, but OpenAPIV3 enabled should fall back to v2 if
|
||||
// v2 is not available. Shows this by making openapiv3 client
|
||||
// point to a bad URL. So the fact the proper data renders is
|
||||
// indication v2 was used instead.
|
||||
Name: "Fallback",
|
||||
Args: []string{"pods"},
|
||||
ExpectPattern: []string{`\s*KIND:[\t ]*Pod\s*`},
|
||||
OpenAPIV3SchemaFn: fallbackV3SchemaFn,
|
||||
},
|
||||
{
|
||||
Name: "NonDefaultAPIVersion",
|
||||
Args: []string{"horizontalpodautoscalers"},
|
||||
Flags: map[string]string{"api-version": "autoscaling/v2"},
|
||||
ExpectPattern: []string{`\s*VERSION:[\t ]*(v2|autoscaling/v2)\s*`},
|
||||
},
|
||||
{
|
||||
// Show that explicitly specifying --output plaintext-openapiv2 causes
|
||||
// old implementation to be used even though OpenAPIV3 is enabled
|
||||
Name: "OutputPlaintextV2",
|
||||
Args: []string{"pods"},
|
||||
Flags: map[string]string{"output": "plaintext-openapiv2"},
|
||||
ExpectPattern: []string{`\s*KIND:[\t ]*Pod\s*`},
|
||||
OpenAPIV3SchemaFn: fallbackV3SchemaFn,
|
||||
},
|
||||
}
|
||||
cases = append(cases, explainV2Cases...)
|
||||
|
||||
cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{cmdutil.ExplainOpenapiV3}, t, func(t *testing.T) {
|
||||
runExplainTestCases(t, cases)
|
||||
})
|
||||
}
|
||||
|
||||
func runExplainTestCases(t *testing.T, cases []explainTestCase) {
|
||||
fakeServer, err := clienttestutil.NewFakeOpenAPIV3Server(filepath.Join(testDataPath, "openapi", "v3"))
|
||||
if err != nil {
|
||||
t.Fatalf("error starting fake openapi server: %v", err.Error())
|
||||
}
|
||||
defer fakeServer.HttpServer.Close()
|
||||
|
||||
openapiV3SchemaFn := func() (openapiclient.Client, error) {
|
||||
fakeDiscoveryClient := discovery.NewDiscoveryClientForConfigOrDie(&rest.Config{Host: fakeServer.HttpServer.URL})
|
||||
return fakeDiscoveryClient.OpenAPIV3(), nil
|
||||
}
|
||||
|
||||
tf := cmdtesting.NewTestFactory()
|
||||
defer tf.Cleanup()
|
||||
|
||||
|
@ -127,29 +210,72 @@ func TestExplain(t *testing.T) {
|
|||
tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
|
||||
|
||||
ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdExplain("kubectl", tf, ioStreams)
|
||||
cmd.Run(cmd, []string{"pods"})
|
||||
if !strings.Contains(buf.String(), "KIND: Pod") {
|
||||
t.Fatalf("expected output should include pod kind")
|
||||
}
|
||||
|
||||
cmd.Flags().Set("recursive", "true")
|
||||
cmd.Run(cmd, []string{"pods"})
|
||||
if !strings.Contains(buf.String(), "KIND: Pod") ||
|
||||
!strings.Contains(buf.String(), "annotations\t<map[string]string>") {
|
||||
t.Fatalf("expected output should include pod kind")
|
||||
}
|
||||
type catchFatal error
|
||||
|
||||
cmd.Flags().Set("api-version", "batch/v1")
|
||||
cmd.Run(cmd, []string{"cronjobs"})
|
||||
if !strings.Contains(buf.String(), "VERSION: batch/v1") {
|
||||
t.Fatalf("expected output should include pod batch/v1")
|
||||
}
|
||||
for _, tcase := range cases {
|
||||
|
||||
cmd.Flags().Set("api-version", "batch/v1beta1")
|
||||
cmd.Run(cmd, []string{"cronjobs"})
|
||||
if !strings.Contains(buf.String(), "VERSION: batch/v1beta1") {
|
||||
t.Fatalf("expected output should include pod batch/v1beta1")
|
||||
t.Run(tcase.Name, func(t *testing.T) {
|
||||
|
||||
// Catch os.Exit calls for tests which expect them
|
||||
// and replace them with panics that we catch in each test
|
||||
// to check if it is expected.
|
||||
cmdutil.BehaviorOnFatal(func(str string, code int) {
|
||||
panic(catchFatal(errors.New(str)))
|
||||
})
|
||||
defer cmdutil.DefaultBehaviorOnFatal()
|
||||
|
||||
var err error
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
// Catch panic and check at end of test if it is
|
||||
// expected.
|
||||
if panicErr := recover(); panicErr != nil {
|
||||
if e := panicErr.(catchFatal); e != nil {
|
||||
err = e
|
||||
} else {
|
||||
panic(panicErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if tcase.OpenAPIV3SchemaFn != nil {
|
||||
tf.OpenAPIV3ClientFunc = tcase.OpenAPIV3SchemaFn
|
||||
} else {
|
||||
tf.OpenAPIV3ClientFunc = openapiV3SchemaFn
|
||||
}
|
||||
|
||||
cmd := explain.NewCmdExplain("kubectl", tf, ioStreams)
|
||||
for k, v := range tcase.Flags {
|
||||
if err := cmd.Flags().Set(k, v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
cmd.Run(cmd, tcase.Args)
|
||||
}()
|
||||
|
||||
for _, rexp := range tcase.ExpectPattern {
|
||||
if matched, err := regexp.MatchString(rexp, buf.String()); err != nil || !matched {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Errorf("expected output to match regex:\n\t%s\ninstead got:\n\t%s", rexp, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if matched, regexErr := regexp.MatchString(tcase.ExpectErrorPattern, err.Error()); len(tcase.ExpectErrorPattern) == 0 || regexErr != nil || !matched {
|
||||
t.Fatalf("unexpected error: %s did not match regex %s (%v)", err.Error(),
|
||||
tcase.ExpectErrorPattern, regexErr)
|
||||
}
|
||||
} else if len(tcase.ExpectErrorPattern) > 0 {
|
||||
t.Fatalf("did not trigger expected error: %s in output:\n%s", tcase.ExpectErrorPattern, buf.String())
|
||||
}
|
||||
})
|
||||
|
||||
buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,11 +287,11 @@ func TestAlphaEnablement(t *testing.T) {
|
|||
f := cmdtesting.NewTestFactory()
|
||||
defer f.Cleanup()
|
||||
|
||||
cmd := NewCmdExplain("kubectl", f, genericclioptions.NewTestIOStreamsDiscard())
|
||||
cmd := explain.NewCmdExplain("kubectl", f, genericclioptions.NewTestIOStreamsDiscard())
|
||||
require.Nil(t, cmd.Flags().Lookup(flag), "flag %q should not be registered without the %q feature enabled", flag, feature)
|
||||
|
||||
cmdtesting.WithAlphaEnvs([]cmdutil.FeatureGate{feature}, t, func(t *testing.T) {
|
||||
cmd := NewCmdExplain("kubectl", f, genericclioptions.NewTestIOStreamsDiscard())
|
||||
cmd := explain.NewCmdExplain("kubectl", f, genericclioptions.NewTestIOStreamsDiscard())
|
||||
require.NotNil(t, cmd.Flags().Lookup(flag), "flag %q should be registered with the %q feature enabled", flag, feature)
|
||||
})
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue