1 // -*- fill-column: 75 -*- 2 3 // Copyright 2020 Datawire. All rights reserved 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. You may obtain 7 // a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 // This file deals with common things that are shared between multiple 18 // CRDs, but are ultimately used by individual CRDs (rather than by the 19 // apiVersion as a whole). 20 21 package v3alpha1 22 23 import ( 24 "encoding/json" 25 "time" 26 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 ) 29 30 // V2ExplicitTLS controls some vanity/stylistic elements when converting 31 // from v3alpha1 to v2. The values in an V2ExplicitTLS should not in any 32 // way affect the runtime operation of Emissary; except that it may affect 33 // internal names in the Envoy config, which may in turn affect stats 34 // names. But it should not affect any end-user observable behavior. 35 type V2ExplicitTLS struct { 36 // TLS controls whether and how to represent the "tls" field when 37 // its value could be implied by the "service" field. In v2, there 38 // were a lot of different ways to spell an "empty" value, and this 39 // field specifies which way to spell it (and will therefore only 40 // be used if the value will indeed be empty). 41 // 42 // | Value | Representation | Meaning of representation | 43 // |--------------+---------------------------------------+------------------------------------| 44 // | "" | omit the field | defer to service (no TLSContext) | 45 // | "null" | store an explicit "null" in the field | defer to service (no TLSContext) | 46 // | "string" | store an empty string in the field | defer to service (no TLSContext) | 47 // | "bool:false" | store a Boolean "false" in the field | defer to service (no TLSContext) | 48 // | "bool:true" | store a Boolean "true" in the field | originate TLS (no TLSContext) | 49 // 50 // If the meaning of the representation contradicts anything else 51 // (if a TLSContext is to be used, or in the case of "bool:true" if 52 // TLS is not to be originated), then this field is ignored. 53 // 54 // +kubebuilder:validation:Enum={"","null","bool:true","bool:false","string"} 55 TLS string `json:"tls,omitempty"` 56 57 // ServiceScheme specifies how to spell and capitalize the scheme-part of the 58 // service URL. 59 // 60 // Acceptable values are "http://" (case-insensitive), "https://" 61 // (case-insensitive), or "". The value is used if it agrees with 62 // whether or not this resource enables TLS origination, or if 63 // something else in the resource overrides the scheme. 64 // 65 // +kubebuilder:validation:Pattern="^([hH][tT][tT][pP][sS]?://)?$" 66 ServiceScheme *string `json:"serviceScheme,omitempty"` 67 } 68 69 type CircuitBreaker struct { 70 // +kubebuilder:validation:Enum={"default", "high"} 71 Priority string `json:"priority,omitempty"` 72 MaxConnections *int `json:"max_connections,omitempty"` 73 MaxPendingRequests *int `json:"max_pending_requests,omitempty"` 74 MaxRequests *int `json:"max_requests,omitempty"` 75 MaxRetries *int `json:"max_retries,omitempty"` 76 } 77 78 // ErrorResponseTextFormatSource specifies a source for an error response body 79 type ErrorResponseTextFormatSource struct { 80 // The name of a file on the Ambassador pod that contains a format text string. 81 Filename string `json:"filename"` 82 } 83 84 // ErorrResponseOverrideBody specifies the body of an error response 85 type ErrorResponseOverrideBody struct { 86 // A format string representing a text response body. 87 // Content-Type can be set using the `content_type` field below. 88 ErrorResponseTextFormat *string `json:"text_format,omitempty"` 89 90 // A JSON response with content-type: application/json. The values can 91 // contain format text like in text_format. 92 ErrorResponseJsonFormat *map[string]string `json:"json_format,omitempty"` 93 94 // A format string sourced from a file on the Ambassador container. 95 // Useful for larger response bodies that should not be placed inline 96 // in configuration. 97 ErrorResponseTextFormatSource *ErrorResponseTextFormatSource `json:"text_format_source,omitempty"` 98 99 // The content type to set on the error response body when 100 // using text_format or text_format_source. Defaults to 'text/plain'. 101 ContentType string `json:"content_type,omitempty"` 102 } 103 104 // A response rewrite for an HTTP error response 105 type ErrorResponseOverride struct { 106 // The status code to match on -- not a pointer because it's required. 107 // +kubebuilder:validation:Required 108 // +kubebuilder:validation:Minimum=400 109 // +kubebuilder:validation:Maximum=599 110 OnStatusCode int `json:"on_status_code,omitempty"` 111 112 // The new response body 113 // +kubebuilder:validation:Required 114 Body ErrorResponseOverrideBody `json:"body,omitempty"` 115 } 116 117 // A range of response statuses from Start to End inclusive 118 type StatusRange struct { 119 // Start of the statuses to include. Must be between 100 and 599 (inclusive) 120 // +kubebuilder:validation:Required 121 // +kubebuilder:validation:Minimum=100 122 // +kubebuilder:validation:Maximum=599 123 Min int `json:"min,omitempty"` 124 // End of the statuses to include. Must be between 100 and 599 (inclusive) 125 // +kubebuilder:validation:Required 126 // +kubebuilder:validation:Minimum=100 127 // +kubebuilder:validation:Maximum=599 128 Max int `json:"max,omitempty"` 129 } 130 131 // HealthCheck specifies settings for performing active health checking on upstreams 132 type HealthCheck struct { 133 // Timeout for connecting to the health checking endpoint. Defaults to 3 seconds. 134 Timeout *metav1.Duration `json:"timeout,omitempty"` 135 // Interval between health checks. Defaults to every 5 seconds. 136 Interval *metav1.Duration `json:"interval,omitempty"` 137 // Number of non-expected responses for the upstream to be considered unhealthy. A single 503 will mark the upstream as unhealthy regardless of the threshold. Defaults to 2. 138 UnhealthyThreshold *int `json:"unhealthy_threshold,omitempty"` 139 // Number of expected responses for the upstream to be considered healthy. Defaults to 1. 140 HealthyThreshold *int `json:"healthy_threshold,omitempty"` 141 142 // Configuration for where the healthcheck request should be made to 143 // +kubebuilder:validation:Required 144 HealthCheckLocation HealthCheckLocation `json:"health_check,omitempty"` 145 } 146 147 // +kubebuilder:validation:MinProperties=1 148 // +kubebuilder:validation:MaxProperties=1 149 type HealthCheckLocation struct { 150 HTTPHealthCheck *HTTPHealthCheck `json:"http,omitempty"` 151 GRPCHealthCheck *GRPCHealthCheck `json:"grpc,omitempty"` 152 } 153 154 // HealthCheck for HTTP upstreams. Only one of http_health_check or grpc_health_check may be specified 155 type HTTPHealthCheck struct { 156 // +kubebuilder:validation:Required 157 Path string `json:"path,omitempty"` 158 Host string `json:"hostname,omitempty"` 159 AddRequestHeaders map[string]AddedHeader `json:"add_request_headers,omitempty"` 160 RemoveRequestHeaders []string `json:"remove_request_headers,omitempty"` 161 ExpectedStatuses []StatusRange `json:"expected_statuses,omitempty"` 162 } 163 164 // HealthCheck for gRPC upstreams. Only one of grpc_health_check or http_health_check may be specified 165 type GRPCHealthCheck struct { 166 // The upstream name parameter which will be sent to gRPC service in the health check message 167 // +kubebuilder:validation:Required 168 UpstreamName string `json:"upstream_name,omitempty"` 169 // The value of the :authority header in the gRPC health check request. If left empty the upstream name will be used. 170 Authority string `json:"authority,omitempty"` 171 } 172 173 // AmbassadorID declares which Ambassador instances should pay 174 // attention to this resource. If no value is provided, the default is: 175 // 176 // ambassador_id: 177 // - "default" 178 // 179 // TODO(lukeshu): In v3alpha2, consider renaming all of the `ambassador_id` (singular) fields to 180 // `ambassador_ids` (plural). 181 type AmbassadorID []string 182 183 func (aid AmbassadorID) Matches(envVar string) bool { 184 if len(aid) == 0 { 185 aid = []string{"default"} 186 } 187 for _, item := range aid { 188 if item == envVar { 189 return true 190 } 191 if item == "_automatic_" { 192 // We always pay attention to the "_automatic_" ID -- it gives us a to 193 // easily always include certain configuration resources for Edge Stack. 194 return true 195 } 196 } 197 return false 198 } 199 200 // TODO(lukeshu): In v3alpha2, change all of the `{foo}_ms`/`MillisecondDuration` fields to 201 // `{foo}`/`metav1.Duration`. 202 // 203 // +kubebuilder:validation:Type="integer" 204 type MillisecondDuration struct { 205 time.Duration `json:"-"` 206 } 207 208 func (d *MillisecondDuration) UnmarshalJSON(data []byte) error { 209 if string(data) == "null" { 210 d.Duration = 0 211 return nil 212 } 213 214 var intval int64 215 if err := json.Unmarshal(data, &intval); err != nil { 216 return err 217 } 218 d.Duration = time.Duration(intval) * time.Millisecond 219 return nil 220 } 221 222 func (d MillisecondDuration) MarshalJSON() ([]byte, error) { 223 return json.Marshal(d.Milliseconds()) 224 } 225 226 // TODO(lukeshu): In v3alpha2, change all of the `{foo}s`/`SecondDuration` fields to 227 // `{foo}`/`metav1.Duration`. 228 // 229 // +kubebuilder:validation:Type="integer" 230 type SecondDuration struct { 231 time.Duration `json:"-"` 232 } 233 234 func (d *SecondDuration) UnmarshalJSON(data []byte) error { 235 if string(data) == "null" { 236 d.Duration = 0 237 return nil 238 } 239 240 var intval int64 241 if err := json.Unmarshal(data, &intval); err != nil { 242 return err 243 } 244 d.Duration = time.Duration(intval) * time.Second 245 return nil 246 } 247 248 func (d SecondDuration) MarshalJSON() ([]byte, error) { 249 return json.Marshal(int64(d.Seconds())) 250 } 251 252 // UntypedDict is relatively opaque as a Go type, but it preserves its 253 // contents in a roundtrippable way. 254 // 255 // +kubebuilder:validation:Type="object" 256 // +kubebuilder:pruning:PreserveUnknownFields 257 type UntypedDict struct { 258 // We have to hide this from controller-gen inside of a struct 259 // (instead of just `type UntypedDict map[string]json.RawMessage`) 260 // so that controller-gen doesn't generate an `items` field in the 261 // schema. 262 Values map[string]json.RawMessage `json:"-"` 263 } 264 265 func (u UntypedDict) MarshalJSON() ([]byte, error) { 266 return json.Marshal(u.Values) 267 } 268 269 func (u *UntypedDict) UnmarshalJSON(data []byte) error { 270 return json.Unmarshal(data, &u.Values) 271 } 272