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  }