knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/apis/duck/v1beta1/destination.go (about) 1 /* 2 Copyright 2019 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 v1beta1 18 19 import ( 20 "context" 21 "crypto/x509" 22 "encoding/pem" 23 24 corev1 "k8s.io/api/core/v1" 25 "knative.dev/pkg/apis" 26 ) 27 28 // Destination represents a target of an invocation over HTTP. 29 type Destination struct { 30 // Ref points to an Addressable. 31 // +optional 32 Ref *corev1.ObjectReference `json:"ref,omitempty"` 33 34 // +optional 35 DeprecatedAPIVersion string `json:"apiVersion,omitempty"` 36 37 // +optional 38 DeprecatedKind string `json:"kind,omitempty"` 39 40 // +optional 41 DeprecatedName string `json:"name,omitempty"` 42 43 // +optional 44 DeprecatedNamespace string `json:"namespace,omitempty"` 45 46 // URI can be an absolute URL(non-empty scheme and non-empty host) pointing to the target or a relative URI. Relative URIs will be resolved using the base URI retrieved from Ref. 47 // +optional 48 URI *apis.URL `json:"uri,omitempty"` 49 50 // CACerts are Certification Authority (CA) certificates in PEM format 51 // according to https://www.rfc-editor.org/rfc/rfc7468. 52 // If set, these CAs are appended to the set of CAs provided 53 // by the Addressable target, if any. 54 // +optional 55 CACerts *string `json:"CACerts,omitempty"` 56 } 57 58 func (dest *Destination) Validate(ctx context.Context) *apis.FieldError { 59 if dest == nil { 60 return nil 61 } 62 return ValidateDestination(*dest, true).ViaField(apis.CurrentField) 63 } 64 65 func (dest *Destination) ValidateDisallowDeprecated(ctx context.Context) *apis.FieldError { 66 if dest == nil { 67 return nil 68 } 69 return ValidateDestination(*dest, false).ViaField(apis.CurrentField) 70 } 71 72 // ValidateDestination validates Destination and either allows or disallows 73 // Deprecated* fields depending on the flag. 74 func ValidateDestination(dest Destination, allowDeprecatedFields bool) *apis.FieldError { 75 if !allowDeprecatedFields { 76 var errs *apis.FieldError 77 if dest.DeprecatedAPIVersion != "" { 78 errs = errs.Also(apis.ErrInvalidValue("apiVersion is not allowed here, it's a deprecated value", "apiVersion")) 79 } 80 if dest.DeprecatedKind != "" { 81 errs = errs.Also(apis.ErrInvalidValue("kind is not allowed here, it's a deprecated value", "kind")) 82 } 83 if dest.DeprecatedName != "" { 84 errs = errs.Also(apis.ErrInvalidValue("name is not allowed here, it's a deprecated value", "name")) 85 } 86 if dest.DeprecatedNamespace != "" { 87 errs = errs.Also(apis.ErrInvalidValue("namespace is not allowed here, it's a deprecated value", "namespace")) 88 } 89 if errs != nil { 90 return errs 91 } 92 } 93 94 deprecatedObjectReference := dest.deprecatedObjectReference() 95 if dest.Ref != nil && deprecatedObjectReference != nil { 96 return apis.ErrGeneric("Ref and [apiVersion, kind, name] can't be both present", "[apiVersion, kind, name]", "ref") 97 } 98 99 var ref *corev1.ObjectReference 100 if dest.Ref != nil { 101 ref = dest.Ref 102 } else { 103 ref = deprecatedObjectReference 104 } 105 if ref == nil && dest.URI == nil { 106 return apis.ErrGeneric("expected at least one, got none", "[apiVersion, kind, name]", "ref", "uri") 107 } 108 109 if ref != nil && dest.URI != nil && dest.URI.URL().IsAbs() { 110 return apis.ErrGeneric("Absolute URI is not allowed when Ref or [apiVersion, kind, name] is present", "[apiVersion, kind, name]", "ref", "uri") 111 } 112 // IsAbs() check whether the URL has a non-empty scheme. Besides the non-empty scheme, we also require dest.URI has a non-empty host 113 if ref == nil && dest.URI != nil && (!dest.URI.URL().IsAbs() || dest.URI.Host == "") { 114 return apis.ErrInvalidValue("Relative URI is not allowed when Ref and [apiVersion, kind, name] is absent", "uri") 115 } 116 if ref != nil && dest.URI == nil { 117 if dest.Ref != nil { 118 return validateDestinationRef(*ref).ViaField("ref") 119 } 120 return validateDestinationRef(*ref) 121 } 122 if dest.CACerts != nil { 123 return validateCACerts(dest.CACerts) 124 } 125 return nil 126 } 127 128 func (dest Destination) deprecatedObjectReference() *corev1.ObjectReference { 129 if dest.DeprecatedAPIVersion == "" && dest.DeprecatedKind == "" && dest.DeprecatedName == "" && dest.DeprecatedNamespace == "" { 130 return nil 131 } 132 return &corev1.ObjectReference{ 133 Kind: dest.DeprecatedKind, 134 APIVersion: dest.DeprecatedAPIVersion, 135 Name: dest.DeprecatedName, 136 Namespace: dest.DeprecatedNamespace, 137 } 138 } 139 140 // GetRef gets the ObjectReference from this Destination, if one is present. If no ref is present, 141 // then nil is returned. 142 // Note: this mostly exists to abstract away the deprecated ObjectReference fields. Once they are 143 // removed, then this method should probably be removed too. 144 func (dest *Destination) GetRef() *corev1.ObjectReference { 145 if dest == nil { 146 return nil 147 } 148 if dest.Ref != nil { 149 return dest.Ref 150 } 151 return dest.deprecatedObjectReference() 152 } 153 154 func validateDestinationRef(ref corev1.ObjectReference) *apis.FieldError { 155 // Check the object. 156 var errs *apis.FieldError 157 // Required Fields 158 if ref.Name == "" { 159 errs = errs.Also(apis.ErrMissingField("name")) 160 } 161 if ref.APIVersion == "" { 162 errs = errs.Also(apis.ErrMissingField("apiVersion")) 163 } 164 if ref.Kind == "" { 165 errs = errs.Also(apis.ErrMissingField("kind")) 166 } 167 168 return errs 169 } 170 171 func validateCACerts(caCert *string) *apis.FieldError { 172 // Check the object. 173 var errs *apis.FieldError 174 175 block, err := pem.Decode([]byte(*caCert)) 176 if err != nil && block == nil { 177 errs = errs.Also(apis.ErrInvalidValue("CA Cert provided is invalid", "caCert")) 178 return errs 179 } 180 if block.Type != "CERTIFICATE" { 181 errs = errs.Also(apis.ErrInvalidValue("CA Cert provided is not a certificate", "caCert")) 182 } else if _, err := x509.ParseCertificate(block.Bytes); err != nil { 183 errs = errs.Also(apis.ErrInvalidValue("CA Cert provided is invalid", "caCert")) 184 } 185 return errs 186 }