Merge pull request #57958 from nikhita/sample-controller-crd-validation

Automatic merge from submit-queue (batch tested with PRs 57902, 57958). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Add CustomResourceValidation example in sample-controller

Add `CustomResourceValidation` example in sample-controller.

Addresses the following part of https://github.com/kubernetes/sample-controller/issues/2:

> CRDs support json-schema schemas. These CRDs don't have them. It would be nice to show how to add them

**Release note**:

```release-note
NONE
```

/assign sttts munnerz

Kubernetes-commit: 51acead08489644d30b3f1a978e0b36f228d12a7
This commit is contained in:
Kubernetes Publisher
2018-01-08 03:18:35 -08:00
11 changed files with 235 additions and 179 deletions
+1
View File
@@ -52,6 +52,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
],
)
+18 -2
View File
@@ -20,6 +20,7 @@ import (
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
)
// AmbiguousResourceError is returned if the RESTMapper finds multiple matches for a resource
@@ -85,11 +86,26 @@ func (e *NoResourceMatchError) Error() string {
// NoKindMatchError is returned if the RESTMapper can't find any match for a kind
type NoKindMatchError struct {
PartialKind schema.GroupVersionKind
// GroupKind is the API group and kind that was searched
GroupKind schema.GroupKind
// SearchedVersions is the optional list of versions the search was restricted to
SearchedVersions []string
}
func (e *NoKindMatchError) Error() string {
return fmt.Sprintf("no matches for %v", e.PartialKind)
searchedVersions := sets.NewString()
for _, v := range e.SearchedVersions {
searchedVersions.Insert(schema.GroupVersion{Group: e.GroupKind.Group, Version: v}.String())
}
switch len(searchedVersions) {
case 0:
return fmt.Sprintf("no matches for kind %q in group %q", e.GroupKind.Kind, e.GroupKind.Group)
case 1:
return fmt.Sprintf("no matches for kind %q in version %q", e.GroupKind.Kind, searchedVersions.List()[0])
default:
return fmt.Sprintf("no matches for kind %q in versions %q", e.GroupKind.Kind, searchedVersions.List())
}
}
func IsNoMatchError(err error) bool {
+2 -2
View File
@@ -179,7 +179,7 @@ func (m MultiRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*
if len(errors) > 0 {
return nil, utilerrors.NewAggregate(errors)
}
return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")}
return nil, &NoKindMatchError{GroupKind: gk, SearchedVersions: versions}
}
// RESTMappings returns all possible RESTMappings for the provided group kind, or an error
@@ -204,7 +204,7 @@ func (m MultiRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) (
return nil, utilerrors.NewAggregate(errors)
}
if len(allMappings) == 0 {
return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")}
return nil, &NoKindMatchError{GroupKind: gk, SearchedVersions: versions}
}
return allMappings, nil
}
+1 -1
View File
@@ -153,7 +153,7 @@ func kindMatches(pattern schema.GroupVersionKind, kind schema.GroupVersionKind)
}
func (m PriorityRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
mappings, err := m.Delegate.RESTMappings(gk)
mappings, err := m.Delegate.RESTMappings(gk, versions...)
if err != nil {
return nil, err
}
+2 -2
View File
@@ -472,7 +472,7 @@ func (m *DefaultRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string)
return nil, err
}
if len(mappings) == 0 {
return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")}
return nil, &NoKindMatchError{GroupKind: gk, SearchedVersions: versions}
}
// since we rely on RESTMappings method
// take the first match and return to the caller
@@ -510,7 +510,7 @@ func (m *DefaultRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string
}
if len(potentialGVK) == 0 {
return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")}
return nil, &NoKindMatchError{GroupKind: gk, SearchedVersions: versions}
}
for _, gvk := range potentialGVK {
+12 -12
View File
@@ -1322,23 +1322,23 @@ func mergeMap(original, patch map[string]interface{}, schema LookupPatchMeta, me
// If they're both maps or lists, recurse into the value.
switch originalType.Kind() {
case reflect.Map:
subschema, patchMeta, err := schema.LookupPatchMetadataForStruct(k)
if err != nil {
return nil, err
subschema, patchMeta, err2 := schema.LookupPatchMetadataForStruct(k)
if err2 != nil {
return nil, err2
}
_, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
if err != nil {
return nil, err
_, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
if err2 != nil {
return nil, err2
}
original[k], err = mergeMapHandler(original[k], patchV, subschema, patchStrategy, mergeOptions)
case reflect.Slice:
subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(k)
if err != nil {
return nil, err
subschema, patchMeta, err2 := schema.LookupPatchMetadataForSlice(k)
if err2 != nil {
return nil, err2
}
_, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
if err != nil {
return nil, err
_, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
if err2 != nil {
return nil, err2
}
original[k], err = mergeSliceHandler(original[k], patchV, subschema, patchStrategy, patchMeta.GetPatchMergeKey(), isDeleteList, mergeOptions)
default:
+12 -2
View File
@@ -63,8 +63,18 @@ type Getter interface {
// NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace and field selector.
func NewListWatchFromClient(c Getter, resource string, namespace string, fieldSelector fields.Selector) *ListWatch {
listFunc := func(options metav1.ListOptions) (runtime.Object, error) {
optionsModifier := func(options *metav1.ListOptions) {
options.FieldSelector = fieldSelector.String()
}
return NewFilteredListWatchFromClient(c, resource, namespace, optionsModifier)
}
// NewFilteredListWatchFromClient creates a new ListWatch from the specified client, resource, namespace, and option modifier.
// Option modifier is a function takes a ListOptions and modifies the consumed ListOptions. Provide customized modifier function
// to apply modification to ListOptions with a field selector, a label selector, or any other desired options.
func NewFilteredListWatchFromClient(c Getter, resource string, namespace string, optionsModifier func(options *metav1.ListOptions)) *ListWatch {
listFunc := func(options metav1.ListOptions) (runtime.Object, error) {
optionsModifier(&options)
return c.Get().
Namespace(namespace).
Resource(resource).
@@ -74,7 +84,7 @@ func NewListWatchFromClient(c Getter, resource string, namespace string, fieldSe
}
watchFunc := func(options metav1.ListOptions) (watch.Interface, error) {
options.Watch = true
options.FieldSelector = fieldSelector.String()
optionsModifier(&options)
return c.Get().
Namespace(namespace).
Resource(resource).
+4
View File
@@ -253,6 +253,10 @@ func validateAuthInfo(authInfoName string, authInfo clientcmdapi.AuthInfo) []err
func validateContext(contextName string, context clientcmdapi.Context, config clientcmdapi.Config) []error {
validationErrors := make([]error, 0)
if len(contextName) == 0 {
validationErrors = append(validationErrors, fmt.Errorf("empty context name for %#v is not allowed", context))
}
if len(context.AuthInfo) == 0 {
validationErrors = append(validationErrors, fmt.Errorf("user was not specified for context %q", contextName))
} else if _, exists := config.AuthInfos[context.AuthInfo]; !exists {