mirror of https://github.com/knative/caching.git
This adds context.Context to the interfaces used by the pkg webhook. (#23)
See also: https://github.com/knative/pkg/pull/332
This commit is contained in:
parent
b9d80eaec5
commit
3fc06fd3c9
|
@ -136,15 +136,14 @@
|
|||
revision = "f2b4162afba35581b6d4a50d3b8f34e33c144682"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:0f5c0a93ea5a62ea721282a07746832546580707042f705710f6e9f9778a0598"
|
||||
digest = "1:fa6e19b10f3088d6f290e32ba2f9735d2810dd8e6544028d4d0c842c162b83ef"
|
||||
name = "github.com/knative/pkg"
|
||||
packages = [
|
||||
"apis",
|
||||
"kmeta",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "2a7e950c4e2d635d6e32ba3d050dd32ba7be5be3"
|
||||
revision = "60fdcbcabd2faeb34328d8b2725dc76c59189453"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
|
|
@ -28,6 +28,11 @@ required = [
|
|||
name = "k8s.io/client-go"
|
||||
version = "kubernetes-1.12.6"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/knative/pkg"
|
||||
# HEAD as of 2019-03-21
|
||||
revision = "60fdcbcabd2faeb34328d8b2725dc76c59189453"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
|
|
@ -16,6 +16,8 @@ limitations under the License.
|
|||
|
||||
package v1alpha1
|
||||
|
||||
func (r *Image) SetDefaults() {
|
||||
import "context"
|
||||
|
||||
func (r *Image) SetDefaults(ctx context.Context) {
|
||||
// TODO(mattmoor): This
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
@ -36,7 +37,7 @@ func TestImageDefaulting(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := test.in
|
||||
got.SetDefaults()
|
||||
got.SetDefaults(context.Background())
|
||||
if diff := cmp.Diff(test.want, got); diff != "" {
|
||||
t.Errorf("SetDefaults (-want, +got) = %v", diff)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
@ -25,11 +26,11 @@ import (
|
|||
"github.com/knative/pkg/apis"
|
||||
)
|
||||
|
||||
func (rt *Image) Validate() *apis.FieldError {
|
||||
return rt.Spec.Validate().ViaField("spec")
|
||||
func (rt *Image) Validate(ctx context.Context) *apis.FieldError {
|
||||
return rt.Spec.Validate(ctx).ViaField("spec")
|
||||
}
|
||||
|
||||
func (rs *ImageSpec) Validate() *apis.FieldError {
|
||||
func (rs *ImageSpec) Validate(ctx context.Context) *apis.FieldError {
|
||||
if rs.Image == "" {
|
||||
return apis.ErrMissingField("image")
|
||||
}
|
||||
|
|
|
@ -17,9 +17,9 @@ limitations under the License.
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/knative/pkg/apis"
|
||||
|
@ -55,9 +55,12 @@ func TestImageValidation(t *testing.T) {
|
|||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := test.r.Validate()
|
||||
if diff := cmp.Diff(test.want, got); diff != "" {
|
||||
t.Errorf("Validate (-want, +got) = %v", diff)
|
||||
got := test.r.Validate(context.Background())
|
||||
if (test.want == nil) != (got == nil) {
|
||||
t.Fatalf("Validate() = %v, wanted %v", got, test.want)
|
||||
}
|
||||
if want, got := test.want.Error(), got.Error(); want != got {
|
||||
t.Fatalf("Validate() = %v, wanted %v", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package apis
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -28,12 +29,15 @@ const CurrentField = ""
|
|||
// FieldError is used to propagate the context of errors pertaining to
|
||||
// specific fields in a manner suitable for use in a recursive walk, so
|
||||
// that errors contain the appropriate field context.
|
||||
// +k8s:deepcopy-gen=false
|
||||
// FieldError methods are non-mutating.
|
||||
// +k8s:deepcopy-gen=true
|
||||
type FieldError struct {
|
||||
Message string
|
||||
Paths []string
|
||||
// Details contains an optional longer payload.
|
||||
// +optional
|
||||
Details string
|
||||
errors []FieldError
|
||||
}
|
||||
|
||||
// FieldError implements error
|
||||
|
@ -50,27 +54,234 @@ func (fe *FieldError) ViaField(prefix ...string) *FieldError {
|
|||
if fe == nil {
|
||||
return nil
|
||||
}
|
||||
var newPaths []string
|
||||
// Copy over message and details, paths will be updated and errors come
|
||||
// along using .Also().
|
||||
newErr := &FieldError{
|
||||
Message: fe.Message,
|
||||
Details: fe.Details,
|
||||
}
|
||||
|
||||
// Prepend the Prefix to existing errors.
|
||||
newPaths := make([]string, 0, len(fe.Paths))
|
||||
for _, oldPath := range fe.Paths {
|
||||
if oldPath == CurrentField {
|
||||
newPaths = append(newPaths, strings.Join(prefix, "."))
|
||||
} else {
|
||||
newPaths = append(newPaths,
|
||||
strings.Join(append(prefix, oldPath), "."))
|
||||
newPaths = append(newPaths, flatten(append(prefix, oldPath)))
|
||||
}
|
||||
newErr.Paths = newPaths
|
||||
for _, e := range fe.errors {
|
||||
newErr = newErr.Also(e.ViaField(prefix...))
|
||||
}
|
||||
return newErr
|
||||
}
|
||||
|
||||
// ViaIndex is used to attach an index to the next ViaField provided.
|
||||
// For example, if a type recursively validates a parameter that has a collection:
|
||||
// for i, c := range spec.Collection {
|
||||
// if err := doValidation(c); err != nil {
|
||||
// return err.ViaIndex(i).ViaField("collection")
|
||||
// }
|
||||
// }
|
||||
func (fe *FieldError) ViaIndex(index int) *FieldError {
|
||||
return fe.ViaField(asIndex(index))
|
||||
}
|
||||
|
||||
// ViaFieldIndex is the short way to chain: err.ViaIndex(bar).ViaField(foo)
|
||||
func (fe *FieldError) ViaFieldIndex(field string, index int) *FieldError {
|
||||
return fe.ViaIndex(index).ViaField(field)
|
||||
}
|
||||
|
||||
// ViaKey is used to attach a key to the next ViaField provided.
|
||||
// For example, if a type recursively validates a parameter that has a collection:
|
||||
// for k, v := range spec.Bag. {
|
||||
// if err := doValidation(v); err != nil {
|
||||
// return err.ViaKey(k).ViaField("bag")
|
||||
// }
|
||||
// }
|
||||
func (fe *FieldError) ViaKey(key string) *FieldError {
|
||||
return fe.ViaField(asKey(key))
|
||||
}
|
||||
|
||||
// ViaFieldKey is the short way to chain: err.ViaKey(bar).ViaField(foo)
|
||||
func (fe *FieldError) ViaFieldKey(field string, key string) *FieldError {
|
||||
return fe.ViaKey(key).ViaField(field)
|
||||
}
|
||||
|
||||
// Also collects errors, returns a new collection of existing errors and new errors.
|
||||
func (fe *FieldError) Also(errs ...*FieldError) *FieldError {
|
||||
var newErr *FieldError
|
||||
// collect the current objects errors, if it has any
|
||||
if !fe.isEmpty() {
|
||||
newErr = fe.DeepCopy()
|
||||
} else {
|
||||
newErr = &FieldError{}
|
||||
}
|
||||
// and then collect the passed in errors
|
||||
for _, e := range errs {
|
||||
if !e.isEmpty() {
|
||||
newErr.errors = append(newErr.errors, *e)
|
||||
}
|
||||
}
|
||||
fe.Paths = newPaths
|
||||
return fe
|
||||
if newErr.isEmpty() {
|
||||
return nil
|
||||
}
|
||||
return newErr
|
||||
}
|
||||
|
||||
func (fe *FieldError) isEmpty() bool {
|
||||
if fe == nil {
|
||||
return true
|
||||
}
|
||||
return fe.Message == "" && fe.Details == "" && len(fe.errors) == 0 && len(fe.Paths) == 0
|
||||
}
|
||||
|
||||
// normalized returns a flattened copy of all the errors.
|
||||
func (fe *FieldError) normalized() []*FieldError {
|
||||
// In case we call normalized on a nil object, return just an empty
|
||||
// list. This can happen when .Error() is called on a nil object.
|
||||
if fe == nil {
|
||||
return []*FieldError(nil)
|
||||
}
|
||||
|
||||
// Allocate errors with at least as many objects as we'll get on the first pass.
|
||||
errors := make([]*FieldError, 0, len(fe.errors)+1)
|
||||
// If this FieldError is a leaf, add it.
|
||||
if fe.Message != "" {
|
||||
errors = append(errors, &FieldError{
|
||||
Message: fe.Message,
|
||||
Paths: fe.Paths,
|
||||
Details: fe.Details,
|
||||
})
|
||||
}
|
||||
// And then collect all other errors recursively.
|
||||
for _, e := range fe.errors {
|
||||
errors = append(errors, e.normalized()...)
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
// Error implements error
|
||||
func (fe *FieldError) Error() string {
|
||||
if fe.Details == "" {
|
||||
return fmt.Sprintf("%v: %v", fe.Message, strings.Join(fe.Paths, ", "))
|
||||
// Get the list of errors as a flat merged list.
|
||||
normedErrors := merge(fe.normalized())
|
||||
errs := make([]string, 0, len(normedErrors))
|
||||
for _, e := range normedErrors {
|
||||
if e.Details == "" {
|
||||
errs = append(errs, fmt.Sprintf("%v: %v", e.Message, strings.Join(e.Paths, ", ")))
|
||||
} else {
|
||||
errs = append(errs, fmt.Sprintf("%v: %v\n%v", e.Message, strings.Join(e.Paths, ", "), e.Details))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%v: %v\n%v", fe.Message, strings.Join(fe.Paths, ", "), fe.Details)
|
||||
return strings.Join(errs, "\n")
|
||||
}
|
||||
|
||||
// Helpers ---
|
||||
|
||||
func asIndex(index int) string {
|
||||
return fmt.Sprintf("[%d]", index)
|
||||
}
|
||||
|
||||
func isIndex(part string) bool {
|
||||
return strings.HasPrefix(part, "[") && strings.HasSuffix(part, "]")
|
||||
}
|
||||
|
||||
func asKey(key string) string {
|
||||
return fmt.Sprintf("[%s]", key)
|
||||
}
|
||||
|
||||
// flatten takes in a array of path components and looks for chances to flatten
|
||||
// objects that have index prefixes, examples:
|
||||
// err([0]).ViaField(bar).ViaField(foo) -> foo.bar.[0] converts to foo.bar[0]
|
||||
// err(bar).ViaIndex(0).ViaField(foo) -> foo.[0].bar converts to foo[0].bar
|
||||
// err(bar).ViaField(foo).ViaIndex(0) -> [0].foo.bar converts to [0].foo.bar
|
||||
// err(bar).ViaIndex(0).ViaIndex(1).ViaField(foo) -> foo.[1].[0].bar converts to foo[1][0].bar
|
||||
func flatten(path []string) string {
|
||||
var newPath []string
|
||||
for _, part := range path {
|
||||
for _, p := range strings.Split(part, ".") {
|
||||
if p == CurrentField {
|
||||
continue
|
||||
} else if len(newPath) > 0 && isIndex(p) {
|
||||
newPath[len(newPath)-1] += p
|
||||
} else {
|
||||
newPath = append(newPath, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.Join(newPath, ".")
|
||||
}
|
||||
|
||||
// mergePaths takes in two string slices and returns the combination of them
|
||||
// without any duplicate entries.
|
||||
func mergePaths(a, b []string) []string {
|
||||
newPaths := make([]string, 0, len(a)+len(b))
|
||||
newPaths = append(newPaths, a...)
|
||||
for _, bi := range b {
|
||||
if !containsString(newPaths, bi) {
|
||||
newPaths = append(newPaths, bi)
|
||||
}
|
||||
}
|
||||
return newPaths
|
||||
}
|
||||
|
||||
// containsString takes in a string slice and looks for the provided string
|
||||
// within the slice.
|
||||
func containsString(slice []string, s string) bool {
|
||||
for _, item := range slice {
|
||||
if item == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// merge takes in a flat list of FieldErrors and returns back a merged list of
|
||||
// FieldErrors. FieldErrors have their Paths combined (and de-duped) if their
|
||||
// Message and Details are the same. Merge will not inspect FieldError.errors.
|
||||
// Merge will also sort the .Path slice, and the errors slice before returning.
|
||||
func merge(errs []*FieldError) []*FieldError {
|
||||
// make a map big enough for all the errors.
|
||||
m := make(map[string]*FieldError, len(errs))
|
||||
|
||||
// Convert errs to a map where the key is <message>-<details> and the value
|
||||
// is the error. If an error already exists in the map with the same key,
|
||||
// then the paths will be merged.
|
||||
for _, e := range errs {
|
||||
k := key(e)
|
||||
if v, ok := m[k]; ok {
|
||||
// Found a match, merge the keys.
|
||||
v.Paths = mergePaths(v.Paths, e.Paths)
|
||||
} else {
|
||||
// Does not exist in the map, save the error.
|
||||
m[k] = e
|
||||
}
|
||||
}
|
||||
|
||||
// Take the map made previously and flatten it back out again.
|
||||
newErrs := make([]*FieldError, 0, len(m))
|
||||
for _, v := range m {
|
||||
// While we have access to the merged paths, sort them too.
|
||||
sort.Slice(v.Paths, func(i, j int) bool { return v.Paths[i] < v.Paths[j] })
|
||||
newErrs = append(newErrs, v)
|
||||
}
|
||||
|
||||
// Sort the flattened map.
|
||||
sort.Slice(newErrs, func(i, j int) bool {
|
||||
if newErrs[i].Message == newErrs[j].Message {
|
||||
return newErrs[i].Details < newErrs[j].Details
|
||||
}
|
||||
return newErrs[i].Message < newErrs[j].Message
|
||||
})
|
||||
|
||||
// return back the merged list of sorted errors.
|
||||
return newErrs
|
||||
}
|
||||
|
||||
// key returns the key using the fields .Message and .Details.
|
||||
func key(err *FieldError) string {
|
||||
return fmt.Sprintf("%s-%s", err.Message, err.Details)
|
||||
}
|
||||
|
||||
// Public helpers ---
|
||||
|
||||
// ErrMissingField is a variadic helper method for constructing a FieldError for
|
||||
// a set of missing fields.
|
||||
func ErrMissingField(fieldPaths ...string) *FieldError {
|
||||
|
@ -89,6 +300,12 @@ func ErrDisallowedFields(fieldPaths ...string) *FieldError {
|
|||
}
|
||||
}
|
||||
|
||||
// ErrInvalidArrayValue consturcts a FieldError for a repetetive `field`
|
||||
// at `index` that has received an invalid string value.
|
||||
func ErrInvalidArrayValue(value, field string, index int) *FieldError {
|
||||
return ErrInvalidValue(value, CurrentField).ViaFieldIndex(field, index)
|
||||
}
|
||||
|
||||
// ErrInvalidValue constructs a FieldError for a field that has received an
|
||||
// invalid string value.
|
||||
func ErrInvalidValue(value, fieldPath string) *FieldError {
|
||||
|
@ -116,8 +333,8 @@ func ErrMultipleOneOf(fieldPaths ...string) *FieldError {
|
|||
}
|
||||
}
|
||||
|
||||
// ErrInvalidKeyName is a variadic helper method for constructing a
|
||||
// FieldError that specifies a key name that is invalid.
|
||||
// ErrInvalidKeyName is a variadic helper method for constructing a FieldError
|
||||
// that specifies a key name that is invalid.
|
||||
func ErrInvalidKeyName(value, fieldPath string, details ...string) *FieldError {
|
||||
return &FieldError{
|
||||
Message: fmt.Sprintf("invalid key name %q", value),
|
||||
|
@ -125,3 +342,12 @@ func ErrInvalidKeyName(value, fieldPath string, details ...string) *FieldError {
|
|||
Details: strings.Join(details, ", "),
|
||||
}
|
||||
}
|
||||
|
||||
// ErrOutOfBoundsValue constructs a FieldError for a field that has received an
|
||||
// out of bound value.
|
||||
func ErrOutOfBoundsValue(value, lower, upper, fieldPath string) *FieldError {
|
||||
return &FieldError{
|
||||
Message: fmt.Sprintf("expected %s <= %s <= %s", lower, value, upper),
|
||||
Paths: []string{fieldPath},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,16 +16,23 @@ limitations under the License.
|
|||
|
||||
package apis
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// Defaultable defines an interface for setting the defaults for the
|
||||
// uninitialized fields of this instance.
|
||||
type Defaultable interface {
|
||||
SetDefaults()
|
||||
SetDefaults(context.Context)
|
||||
}
|
||||
|
||||
// Validatable indicates that a particular type may have its fields validated.
|
||||
type Validatable interface {
|
||||
// Validate checks the validity of this types fields.
|
||||
Validate() *FieldError
|
||||
Validate(context.Context) *FieldError
|
||||
}
|
||||
|
||||
// Immutable indicates that a particular type has fields that should
|
||||
|
@ -33,5 +40,18 @@ type Validatable interface {
|
|||
type Immutable interface {
|
||||
// CheckImmutableFields checks that the current instance's immutable
|
||||
// fields haven't changed from the provided original.
|
||||
CheckImmutableFields(original Immutable) *FieldError
|
||||
CheckImmutableFields(ctx context.Context, original Immutable) *FieldError
|
||||
}
|
||||
|
||||
// Listable indicates that a particular type can be returned via the returned
|
||||
// list type by the API server.
|
||||
type Listable interface {
|
||||
runtime.Object
|
||||
|
||||
GetListType() runtime.Object
|
||||
}
|
||||
|
||||
// Annotatable indicates that a particular type applies various annotations.
|
||||
type Annotatable interface {
|
||||
AnnotateUserInfo(ctx context.Context, previous Annotatable, ui *authenticationv1.UserInfo)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
Copyright 2018 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// KindToResource converts a GroupVersionKind to a GroupVersionResource
|
||||
// through the world's simplest (worst) pluralizer.
|
||||
func KindToResource(gvk schema.GroupVersionKind) schema.GroupVersionResource {
|
||||
return schema.GroupVersionResource{
|
||||
Group: gvk.Group,
|
||||
Version: gvk.Version,
|
||||
Resource: pluralizeKind(gvk.Kind),
|
||||
}
|
||||
}
|
||||
|
||||
// Takes a kind and pluralizes it. This is super terrible, but I am
|
||||
// not aware of a generic way to do this.
|
||||
// I am not alone in thinking this and I haven't found a better solution:
|
||||
// This seems relevant:
|
||||
// https://github.com/kubernetes/kubernetes/issues/18622
|
||||
func pluralizeKind(kind string) string {
|
||||
ret := strings.ToLower(kind)
|
||||
if strings.HasSuffix(ret, "s") {
|
||||
return fmt.Sprintf("%ses", ret)
|
||||
}
|
||||
return fmt.Sprintf("%ss", ret)
|
||||
}
|
|
@ -20,6 +20,34 @@ limitations under the License.
|
|||
|
||||
package apis
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FieldError) DeepCopyInto(out *FieldError) {
|
||||
*out = *in
|
||||
if in.Paths != nil {
|
||||
in, out := &in.Paths, &out.Paths
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.errors != nil {
|
||||
in, out := &in.errors, &out.errors
|
||||
*out = make([]FieldError, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FieldError.
|
||||
func (in *FieldError) DeepCopy() *FieldError {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FieldError)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *VolatileTime) DeepCopyInto(out *VolatileTime) {
|
||||
*out = *in
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
Copyright 2018 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package kmeta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
// Accessor is a collection of interfaces from metav1.TypeMeta,
|
||||
// runtime.Object and metav1.Object that Kubernetes API types
|
||||
// registered with runtime.Scheme must support.
|
||||
type Accessor interface {
|
||||
// Interfaces for metav1.TypeMeta
|
||||
GroupVersionKind() schema.GroupVersionKind
|
||||
SetGroupVersionKind(gvk schema.GroupVersionKind)
|
||||
|
||||
// Interfaces for runtime.Object
|
||||
GetObjectKind() schema.ObjectKind
|
||||
DeepCopyObject() runtime.Object
|
||||
|
||||
// Interfaces for metav1.Object
|
||||
GetNamespace() string
|
||||
SetNamespace(namespace string)
|
||||
GetName() string
|
||||
SetName(name string)
|
||||
GetGenerateName() string
|
||||
SetGenerateName(name string)
|
||||
GetUID() types.UID
|
||||
SetUID(uid types.UID)
|
||||
GetResourceVersion() string
|
||||
SetResourceVersion(version string)
|
||||
GetGeneration() int64
|
||||
SetGeneration(generation int64)
|
||||
GetSelfLink() string
|
||||
SetSelfLink(selfLink string)
|
||||
GetCreationTimestamp() metav1.Time
|
||||
SetCreationTimestamp(timestamp metav1.Time)
|
||||
GetDeletionTimestamp() *metav1.Time
|
||||
SetDeletionTimestamp(timestamp *metav1.Time)
|
||||
GetDeletionGracePeriodSeconds() *int64
|
||||
SetDeletionGracePeriodSeconds(*int64)
|
||||
GetLabels() map[string]string
|
||||
SetLabels(labels map[string]string)
|
||||
GetAnnotations() map[string]string
|
||||
SetAnnotations(annotations map[string]string)
|
||||
GetInitializers() *metav1.Initializers
|
||||
SetInitializers(initializers *metav1.Initializers)
|
||||
GetFinalizers() []string
|
||||
SetFinalizers(finalizers []string)
|
||||
GetOwnerReferences() []metav1.OwnerReference
|
||||
SetOwnerReferences([]metav1.OwnerReference)
|
||||
GetClusterName() string
|
||||
SetClusterName(clusterName string)
|
||||
}
|
||||
|
||||
// DeletionHandlingAccessor tries to convert given interface into Accessor first;
|
||||
// and to handle deletion, it try to fetch info from DeletedFinalStateUnknown on failure.
|
||||
// The name is a reference to cache.DeletionHandlingMetaNamespaceKeyFunc
|
||||
func DeletionHandlingAccessor(obj interface{}) (Accessor, error) {
|
||||
accessor, ok := obj.(Accessor)
|
||||
if !ok {
|
||||
// To handle obj deletion, try to fetch info from DeletedFinalStateUnknown.
|
||||
tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Couldn't get Accessor from tombstone %#v", obj)
|
||||
}
|
||||
accessor, ok = tombstone.Obj.(Accessor)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("The object that Tombstone contained is not of kmeta.Accessor %#v", obj)
|
||||
}
|
||||
}
|
||||
|
||||
return accessor, nil
|
||||
}
|
|
@ -26,6 +26,13 @@ import (
|
|||
|
||||
// The methods in this file are used for managing subresources in cases where
|
||||
// a controller instantiates different resources for each version of itself.
|
||||
// There are two sets of methods available here:
|
||||
// * `*VersionLabel*`: these methods act on `metadata.resourceVersion` and
|
||||
// create new labels for EVERY change to the resource (incl. `/status`).
|
||||
// * `*GenerationLabel*`: these methods act on `metadata.generation` and
|
||||
// create new labels for changes to the resource's "spec" (typically, but
|
||||
// some K8s resources change `metadata.generation` for annotations as well
|
||||
// e.g. Deployment).
|
||||
//
|
||||
// For example, if an A might instantiate N B's at version 1 and M B's at
|
||||
// version 2 then it can use MakeVersionLabels to decorate each subresource
|
||||
|
@ -66,6 +73,37 @@ func MakeOldVersionLabelSelector(om metav1.ObjectMetaAccessor) labels.Selector {
|
|||
)
|
||||
}
|
||||
|
||||
// MakeGenerationLabels constructs a set of labels to apply to subresources
|
||||
// instantiated at this version of the parent resource, so that we can
|
||||
// efficiently select them.
|
||||
func MakeGenerationLabels(om metav1.ObjectMetaAccessor) labels.Set {
|
||||
return map[string]string{
|
||||
"controller": string(om.GetObjectMeta().GetUID()),
|
||||
"generation": genStr(om),
|
||||
}
|
||||
}
|
||||
|
||||
// MakeGenerationLabelSelector constructs a selector for subresources
|
||||
// instantiated at this version of the parent resource. This keys
|
||||
// off of the labels populated by MakeGenerationLabels.
|
||||
func MakeGenerationLabelSelector(om metav1.ObjectMetaAccessor) labels.Selector {
|
||||
return labels.SelectorFromSet(MakeGenerationLabels(om))
|
||||
}
|
||||
|
||||
// MakeOldGenerationLabelSelector constructs a selector for subresources
|
||||
// instantiated at an older version of the parent resource. This keys
|
||||
// off of the labels populated by MakeGenerationLabels.
|
||||
func MakeOldGenerationLabelSelector(om metav1.ObjectMetaAccessor) labels.Selector {
|
||||
return labels.NewSelector().Add(
|
||||
mustNewRequirement("controller", selection.Equals, []string{string(om.GetObjectMeta().GetUID())}),
|
||||
mustNewRequirement("generation", selection.NotEquals, []string{genStr(om)}),
|
||||
)
|
||||
}
|
||||
|
||||
func genStr(om metav1.ObjectMetaAccessor) string {
|
||||
return fmt.Sprintf("%05d", om.GetObjectMeta().GetGeneration())
|
||||
}
|
||||
|
||||
// mustNewRequirement panics if there are any errors constructing our selectors.
|
||||
func mustNewRequirement(key string, op selection.Operator, vals []string) labels.Requirement {
|
||||
r, err := labels.NewRequirement(key, op, vals)
|
||||
|
|
Loading…
Reference in New Issue