mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-10 21:50:05 +00:00
Merge pull request #2517 from brendandburns/kubecfg
Add client side validation to kubecfg and kubectl.
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
@@ -36,6 +37,7 @@ import (
|
||||
type Factory struct {
|
||||
Mapper meta.RESTMapper
|
||||
Typer runtime.ObjectTyper
|
||||
Validator func(*cobra.Command) (validation.Schema, error)
|
||||
Client func(*cobra.Command, *meta.RESTMapping) (kubectl.RESTClient, error)
|
||||
Describer func(*cobra.Command, *meta.RESTMapping) (kubectl.Describer, error)
|
||||
Printer func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error)
|
||||
@@ -46,6 +48,13 @@ func NewFactory() *Factory {
|
||||
return &Factory{
|
||||
Mapper: latest.RESTMapper,
|
||||
Typer: api.Scheme,
|
||||
Validator: func(cmd *cobra.Command) (validation.Schema, error) {
|
||||
if GetFlagBool(cmd, "validate") {
|
||||
return &clientSwaggerSchema{getKubeClient(cmd), api.Scheme}, nil
|
||||
} else {
|
||||
return validation.NullSchema{}, nil
|
||||
}
|
||||
},
|
||||
Client: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) {
|
||||
return getKubeClient(cmd), nil
|
||||
},
|
||||
@@ -87,6 +96,7 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
|
||||
cmds.PersistentFlags().Bool("insecure-skip-tls-verify", false, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.")
|
||||
cmds.PersistentFlags().String("ns-path", os.Getenv("HOME")+"/.kubernetes_ns", "Path to the namespace info file that holds the namespace context to use for CLI requests.")
|
||||
cmds.PersistentFlags().StringP("namespace", "n", "", "If present, the namespace scope for this CLI request.")
|
||||
cmds.PersistentFlags().Bool("validate", false, "If true, use a schema to validate the input before sending it")
|
||||
|
||||
cmds.AddCommand(NewCmdVersion(out))
|
||||
cmds.AddCommand(NewCmdProxy(out))
|
||||
@@ -216,3 +226,28 @@ func getKubeClient(cmd *cobra.Command) *client.Client {
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
type clientSwaggerSchema struct {
|
||||
c *client.Client
|
||||
t runtime.ObjectTyper
|
||||
}
|
||||
|
||||
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
|
||||
version, _, err := c.t.DataVersionAndKind(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
schemaData, err := c.c.RESTClient.Get().
|
||||
AbsPath("/swaggerapi/api").
|
||||
Path(version).
|
||||
Do().
|
||||
Raw()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return schema.ValidateBytes(data)
|
||||
}
|
||||
|
@@ -43,7 +43,9 @@ Examples:
|
||||
if len(filename) == 0 {
|
||||
usageError(cmd, "Must specify filename to create")
|
||||
}
|
||||
mapping, namespace, name, data := ResourceFromFile(filename, f.Typer, f.Mapper)
|
||||
schema, err := f.Validator(cmd)
|
||||
checkErr(err)
|
||||
mapping, namespace, name, data := ResourceFromFile(filename, f.Typer, f.Mapper, schema)
|
||||
client, err := f.Client(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
|
@@ -50,7 +50,9 @@ Examples:
|
||||
<delete a pod with ID 1234-56-7890-234234-456456>`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
filename := GetFlagString(cmd, "filename")
|
||||
mapping, namespace, name := ResourceFromArgsOrFile(cmd, args, filename, f.Typer, f.Mapper)
|
||||
schema, err := f.Validator(cmd)
|
||||
checkErr(err)
|
||||
mapping, namespace, name := ResourceFromArgsOrFile(cmd, args, filename, f.Typer, f.Mapper, schema)
|
||||
client, err := f.Client(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
|
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
|
||||
@@ -29,7 +30,7 @@ import (
|
||||
// ResourceFromArgsOrFile expects two arguments or a valid file with a given type, and extracts
|
||||
// the fields necessary to uniquely locate a resource. Displays a usageError if that contract is
|
||||
// not satisfied, or a generic error if any other problems occur.
|
||||
func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string) {
|
||||
func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper, schema validation.Schema) (mapping *meta.RESTMapping, namespace, name string) {
|
||||
// If command line args are passed in, use those preferentially.
|
||||
if len(args) > 0 && len(args) != 2 {
|
||||
usageError(cmd, "If passing in command line parameters, must be resource and name")
|
||||
@@ -58,7 +59,7 @@ func ResourceFromArgsOrFile(cmd *cobra.Command, args []string, filename string,
|
||||
usageError(cmd, "Must specify filename or command line params")
|
||||
}
|
||||
|
||||
mapping, namespace, name, _ = ResourceFromFile(filename, typer, mapper)
|
||||
mapping, namespace, name, _ = ResourceFromFile(filename, typer, mapper, schema)
|
||||
if len(name) == 0 {
|
||||
checkErr(fmt.Errorf("the resource in the provided file has no name (or ID) defined"))
|
||||
}
|
||||
@@ -122,7 +123,7 @@ func ResourceOrTypeFromArgs(cmd *cobra.Command, args []string, mapper meta.RESTM
|
||||
// ResourceFromFile retrieves the name and namespace from a valid file. If the file does not
|
||||
// resolve to a known type an error is returned. The returned mapping can be used to determine
|
||||
// the correct REST endpoint to modify this resource with.
|
||||
func ResourceFromFile(filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper) (mapping *meta.RESTMapping, namespace, name string, data []byte) {
|
||||
func ResourceFromFile(filename string, typer runtime.ObjectTyper, mapper meta.RESTMapper, schema validation.Schema) (mapping *meta.RESTMapping, namespace, name string, data []byte) {
|
||||
configData, err := ReadConfigData(filename)
|
||||
checkErr(err)
|
||||
data = configData
|
||||
@@ -135,6 +136,9 @@ func ResourceFromFile(filename string, typer runtime.ObjectTyper, mapper meta.RE
|
||||
checkErr(fmt.Errorf("the resource in the provided file has no apiVersion defined"))
|
||||
}
|
||||
|
||||
err = schema.ValidateBytes(data)
|
||||
checkErr(err)
|
||||
|
||||
mapping, err = mapper.RESTMapping(version, kind)
|
||||
checkErr(err)
|
||||
|
||||
|
@@ -43,7 +43,9 @@ Examples:
|
||||
if len(filename) == 0 {
|
||||
usageError(cmd, "Must specify filename to update")
|
||||
}
|
||||
mapping, namespace, name, data := ResourceFromFile(filename, f.Typer, f.Mapper)
|
||||
schema, err := f.Validator(cmd)
|
||||
checkErr(err)
|
||||
mapping, namespace, name, data := ResourceFromFile(filename, f.Typer, f.Mapper, schema)
|
||||
client, err := f.Client(cmd, mapping)
|
||||
checkErr(err)
|
||||
|
||||
|
Reference in New Issue
Block a user