...

Source file src/github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io/v3alpha1/common.go

Documentation: github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io/v3alpha1

     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  

View as plain text