knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/tracker/interface.go (about) 1 /* 2 Copyright 2018 The Knative Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package tracker 18 19 import ( 20 "context" 21 "strings" 22 23 corev1 "k8s.io/api/core/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/runtime/schema" 26 "k8s.io/apimachinery/pkg/types" 27 "k8s.io/apimachinery/pkg/util/validation" 28 29 "knative.dev/pkg/apis" 30 ) 31 32 // Reference is modeled after corev1.ObjectReference, but omits fields 33 // unsupported by the tracker, and permits us to extend things in 34 // divergent ways. 35 type Reference struct { 36 // API version of the referent. 37 // +optional 38 APIVersion string `json:"apiVersion,omitempty"` 39 40 // Kind of the referent. 41 // +optional 42 Kind string `json:"kind,omitempty"` 43 44 // Namespace of the referent. 45 // +optional 46 Namespace string `json:"namespace,omitempty"` 47 48 // Name of the referent. 49 // Mutually exclusive with Selector. 50 // +optional 51 Name string `json:"name,omitempty"` 52 53 // Selector of the referents. 54 // Mutually exclusive with Name. 55 // +optional 56 Selector *metav1.LabelSelector `json:"selector,omitempty"` 57 } 58 59 // Interface defines the interface through which an object can register 60 // that it is tracking another object by reference. 61 type Interface interface { 62 // Track tells us that "obj" is tracking changes to the 63 // referenced object. 64 // 65 // Deprecated: use TrackReference. 66 Track(ref corev1.ObjectReference, obj interface{}) error 67 68 // Track tells us that "obj" is tracking changes to the 69 // referenced object. 70 TrackReference(ref Reference, obj interface{}) error 71 72 // OnChanged is a callback to register with the InformerFactory 73 // so that we are notified for appropriate object changes. 74 OnChanged(obj interface{}) 75 76 // GetObservers returns the names of all observers for the given 77 // object. 78 GetObservers(obj interface{}) []types.NamespacedName 79 80 // OnDeletedObserver is a callback to register with the InformerFactory 81 // so that we are notified for deletions of a watching parent to 82 // remove the respective tracking. 83 OnDeletedObserver(obj interface{}) 84 } 85 86 // GroupVersionKind returns the GroupVersion of the object referenced. 87 func (ref *Reference) GroupVersionKind() schema.GroupVersionKind { 88 gv, _ := schema.ParseGroupVersion(ref.APIVersion) 89 return schema.GroupVersionKind{ 90 Group: gv.Group, 91 Version: gv.Version, 92 Kind: ref.Kind, 93 } 94 } 95 96 // ObjectReference returns the tracker Reference as an ObjectReference. 97 func (ref *Reference) ObjectReference() corev1.ObjectReference { 98 return corev1.ObjectReference{ 99 APIVersion: ref.APIVersion, 100 Kind: ref.Kind, 101 Namespace: ref.Namespace, 102 Name: ref.Name, 103 } 104 } 105 106 // ValidateObjectReference validates that the Reference uses a subset suitable for 107 // translation to a corev1.ObjectReference. This helper is intended to simplify 108 // validating a particular (narrow) use of tracker.Reference. 109 func (ref *Reference) ValidateObjectReference(ctx context.Context) *apis.FieldError { 110 var errs *apis.FieldError 111 112 // Required fields 113 if ref.APIVersion == "" { 114 errs = errs.Also(apis.ErrMissingField("apiVersion")) 115 } else if verrs := validation.IsQualifiedName(ref.APIVersion); len(verrs) != 0 { 116 errs = errs.Also(apis.ErrInvalidValue(strings.Join(verrs, ", "), "apiVersion")) 117 } 118 if ref.Kind == "" { 119 errs = errs.Also(apis.ErrMissingField("kind")) 120 } else if verrs := validation.IsCIdentifier(ref.Kind); len(verrs) != 0 { 121 errs = errs.Also(apis.ErrInvalidValue(strings.Join(verrs, ", "), "kind")) 122 } 123 if ref.Name == "" { 124 errs = errs.Also(apis.ErrMissingField("name")) 125 } else if verrs := validation.IsDNS1123Label(ref.Name); len(verrs) != 0 { 126 errs = errs.Also(apis.ErrInvalidValue(strings.Join(verrs, ", "), "name")) 127 } 128 if ref.Namespace == "" { 129 errs = errs.Also(apis.ErrMissingField("namespace")) 130 } else if verrs := validation.IsDNS1123Label(ref.Namespace); len(verrs) != 0 { 131 errs = errs.Also(apis.ErrInvalidValue(strings.Join(verrs, ", "), "namespace")) 132 } 133 134 // Disallowed fields in ObjectReference-compatible context. 135 if ref.Selector != nil { 136 errs = errs.Also(apis.ErrDisallowedFields("selector")) 137 } 138 139 return errs 140 } 141 142 func (ref *Reference) Validate(ctx context.Context) *apis.FieldError { 143 var errs *apis.FieldError 144 145 // Required fields 146 if ref.APIVersion == "" { 147 errs = errs.Also(apis.ErrMissingField("apiVersion")) 148 } else if verrs := validation.IsQualifiedName(ref.APIVersion); len(verrs) != 0 { 149 errs = errs.Also(apis.ErrInvalidValue(strings.Join(verrs, ", "), "apiVersion")) 150 } 151 if ref.Kind == "" { 152 errs = errs.Also(apis.ErrMissingField("kind")) 153 } else if verrs := validation.IsCIdentifier(ref.Kind); len(verrs) != 0 { 154 errs = errs.Also(apis.ErrInvalidValue(strings.Join(verrs, ", "), "kind")) 155 } 156 if ref.Namespace == "" { 157 errs = errs.Also(apis.ErrMissingField("namespace")) 158 } else if verrs := validation.IsDNS1123Label(ref.Namespace); len(verrs) != 0 { 159 errs = errs.Also(apis.ErrInvalidValue(strings.Join(verrs, ", "), "namespace")) 160 } 161 162 switch { 163 case ref.Selector != nil && ref.Name != "": 164 errs = errs.Also(apis.ErrMultipleOneOf("selector", "name")) 165 case ref.Selector != nil: 166 _, err := metav1.LabelSelectorAsSelector(ref.Selector) 167 if err != nil { 168 errs = errs.Also(apis.ErrInvalidValue(err.Error(), "selector")) 169 } 170 171 case ref.Name != "": 172 if verrs := validation.IsDNS1123Label(ref.Name); len(verrs) != 0 { 173 errs = errs.Also(apis.ErrInvalidValue(strings.Join(verrs, ", "), "name")) 174 } 175 default: 176 errs = errs.Also(apis.ErrMissingOneOf("selector", "name")) 177 } 178 179 return errs 180 }