...

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

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

     1  // Copyright 2020 Datawire.  All rights reserved
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  ///////////////////////////////////////////////////////////////////////////
    16  // Important: Run "make update-yaml" to regenerate code after modifying
    17  // this file.
    18  ///////////////////////////////////////////////////////////////////////////
    19  
    20  package v2
    21  
    22  import (
    23  	"encoding/json"
    24  	"errors"
    25  	"strings"
    26  
    27  	v3alpha1 "github.com/emissary-ingress/emissary/v3/pkg/api/getambassador.io/v3alpha1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  )
    30  
    31  // MappingSpec defines the desired state of Mapping
    32  type MappingSpec struct {
    33  	AmbassadorID AmbassadorID `json:"ambassador_id,omitempty"`
    34  
    35  	// +kubebuilder:validation:Required
    36  	Prefix      string `json:"prefix,omitempty"`
    37  	PrefixRegex *bool  `json:"prefix_regex,omitempty"`
    38  	PrefixExact *bool  `json:"prefix_exact,omitempty"`
    39  	// +kubebuilder:validation:Required
    40  	Service            string                  `json:"service,omitempty"`
    41  	AddRequestHeaders  *map[string]AddedHeader `json:"add_request_headers,omitempty"`
    42  	AddResponseHeaders *map[string]AddedHeader `json:"add_response_headers,omitempty"`
    43  	AddLinkerdHeaders  *bool                   `json:"add_linkerd_headers,omitempty"`
    44  	AutoHostRewrite    *bool                   `json:"auto_host_rewrite,omitempty"`
    45  	CaseSensitive      *bool                   `json:"case_sensitive,omitempty"`
    46  	Docs               *DocsInfo               `json:"docs,omitempty"`
    47  	DNSType            string                  `json:"dns_type,omitempty"`
    48  	EnableIPv4         *bool                   `json:"enable_ipv4,omitempty"`
    49  	EnableIPv6         *bool                   `json:"enable_ipv6,omitempty"`
    50  	CircuitBreakers    []*CircuitBreaker       `json:"circuit_breakers,omitempty"`
    51  	KeepAlive          *KeepAlive              `json:"keepalive,omitempty"`
    52  	CORS               *CORS                   `json:"cors,omitempty"`
    53  	RetryPolicy        *RetryPolicy            `json:"retry_policy,omitempty"`
    54  	RespectDNSTTL      *bool                   `json:"respect_dns_ttl,omitempty"`
    55  	GRPC               *bool                   `json:"grpc,omitempty"`
    56  	HostRedirect       *bool                   `json:"host_redirect,omitempty"`
    57  	HostRewrite        string                  `json:"host_rewrite,omitempty"`
    58  	Method             string                  `json:"method,omitempty"`
    59  	MethodRegex        *bool                   `json:"method_regex,omitempty"`
    60  	OutlierDetection   string                  `json:"outlier_detection,omitempty"`
    61  	// Path replacement to use when generating an HTTP redirect. Used with `host_redirect`.
    62  	PathRedirect string `json:"path_redirect,omitempty"`
    63  	// Prefix rewrite to use when generating an HTTP redirect. Used with `host_redirect`.
    64  	PrefixRedirect string `json:"prefix_redirect,omitempty"`
    65  	// Prefix regex rewrite to use when generating an HTTP redirect. Used with `host_redirect`.
    66  	RegexRedirect *RegexMap `json:"regex_redirect,omitempty"`
    67  	// The response code to use when generating an HTTP redirect. Defaults to 301. Used with
    68  	// `host_redirect`.
    69  	// +kubebuilder:validation:Enum={301,302,303,307,308}
    70  	RedirectResponseCode         *int                 `json:"redirect_response_code,omitempty"`
    71  	Priority                     string               `json:"priority,omitempty"`
    72  	Precedence                   *int                 `json:"precedence,omitempty"`
    73  	ClusterTag                   string               `json:"cluster_tag,omitempty"`
    74  	RemoveRequestHeaders         *StringOrStringList  `json:"remove_request_headers,omitempty"`
    75  	RemoveResponseHeaders        *StringOrStringList  `json:"remove_response_headers,omitempty"`
    76  	Resolver                     string               `json:"resolver,omitempty"`
    77  	Rewrite                      *string              `json:"rewrite,omitempty"`
    78  	RegexRewrite                 *RegexMap            `json:"regex_rewrite,omitempty"`
    79  	Shadow                       *bool                `json:"shadow,omitempty"`
    80  	ConnectTimeout               *MillisecondDuration `json:"connect_timeout_ms,omitempty"`
    81  	ClusterIdleTimeout           *MillisecondDuration `json:"cluster_idle_timeout_ms,omitempty"`
    82  	ClusterMaxConnectionLifetime *MillisecondDuration `json:"cluster_max_connection_lifetime_ms,omitempty"`
    83  	// The timeout for requests that use this Mapping. Overrides `cluster_request_timeout_ms` set on the Ambassador Module, if it exists.
    84  	Timeout     *MillisecondDuration `json:"timeout_ms,omitempty"`
    85  	IdleTimeout *MillisecondDuration `json:"idle_timeout_ms,omitempty"`
    86  	// +k8s:conversion-gen=false
    87  	TLS *BoolOrString `json:"tls,omitempty"`
    88  
    89  	// +kubebuilder:validation:MinItems=1
    90  	// +k8s:conversion-gen:rename=HealthChecks
    91  	V3HealthChecks []v3alpha1.HealthCheck `json:"v3health_checks,omitempty"`
    92  
    93  	// use_websocket is deprecated, and is equivlaent to setting
    94  	// `allow_upgrade: ["websocket"]`
    95  	DeprecatedUseWebsocket *bool `json:"use_websocket,omitempty"`
    96  
    97  	// A case-insensitive list of the non-HTTP protocols to allow
    98  	// "upgrading" to from HTTP via the "Connection: upgrade"
    99  	// mechanism[1].  After the upgrade, Ambassador does not
   100  	// interpret the traffic, and behaves similarly to how it does
   101  	// for TCPMappings.
   102  	//
   103  	// [1]: https://tools.ietf.org/html/rfc7230#section-6.7
   104  	//
   105  	// For example, if your upstream service supports WebSockets,
   106  	// you would write
   107  	//
   108  	//    allow_upgrade:
   109  	//    - websocket
   110  	//
   111  	// Or if your upstream service supports upgrading from HTTP to
   112  	// SPDY (as the Kubernetes apiserver does for `kubectl exec`
   113  	// functionality), you would write
   114  	//
   115  	//    allow_upgrade:
   116  	//    - spdy/3.1
   117  	AllowUpgrade []string `json:"allow_upgrade,omitempty"`
   118  
   119  	Weight                *int              `json:"weight,omitempty"`
   120  	BypassAuth            *bool             `json:"bypass_auth,omitempty"`
   121  	AuthContextExtensions map[string]string `json:"auth_context_extensions,omitempty"`
   122  	// If true, bypasses any `error_response_overrides` set on the Ambassador module.
   123  	BypassErrorResponseOverrides *bool `json:"bypass_error_response_overrides,omitempty"`
   124  	// Error response overrides for this Mapping. Replaces all of the `error_response_overrides`
   125  	// set on the Ambassador module, if any.
   126  	// +kubebuilder:validation:MinItems=1
   127  	ErrorResponseOverrides []ErrorResponseOverride `json:"error_response_overrides,omitempty"`
   128  	Modules                []UntypedDict           `json:"modules,omitempty"`
   129  	// +k8s:conversion-gen:rename=Hostname
   130  	Host string `json:"host,omitempty"`
   131  	// +k8s:conversion-gen:rename=DeprecatedHostRegex
   132  	HostRegex *bool `json:"host_regex,omitempty"`
   133  	// +k8s:conversion-gen=false
   134  	Headers       map[string]BoolOrString `json:"headers,omitempty"`
   135  	RegexHeaders  map[string]string       `json:"regex_headers,omitempty"`
   136  	Labels        DomainMap               `json:"labels,omitempty"`
   137  	EnvoyOverride *UntypedDict            `json:"envoy_override,omitempty"`
   138  	LoadBalancer  *LoadBalancer           `json:"load_balancer,omitempty"`
   139  	// +k8s:conversion-gen=false
   140  	QueryParameters      map[string]BoolOrString `json:"query_parameters,omitempty"`
   141  	RegexQueryParameters map[string]string       `json:"regex_query_parameters,omitempty"`
   142  
   143  	// +k8s:conversion-gen:rename=StatsName
   144  	V3StatsName string `json:"v3StatsName,omitempty"`
   145  }
   146  
   147  type RegexMap struct {
   148  	Pattern      string `json:"pattern,omitempty"`
   149  	Substitution string `json:"substitution,omitempty"`
   150  }
   151  
   152  // DocsInfo provides some extra information about the docs for the Mapping
   153  // (used by the Dev Portal)
   154  type DocsInfo struct {
   155  	Path        string               `json:"path,omitempty"`
   156  	URL         string               `json:"url,omitempty"`
   157  	Ignored     *bool                `json:"ignored,omitempty"`
   158  	DisplayName string               `json:"display_name,omitempty"`
   159  	Timeout     *MillisecondDuration `json:"timeout_ms,omitempty"`
   160  }
   161  
   162  // These are separate types partly because it makes it easier to think about
   163  // how `DomainMap` is built up, but also because it's pretty awful to read
   164  // a type definition that's four or five levels deep with maps and arrays.
   165  
   166  // A DomainMap is the overall Mapping.spec.Labels type. It maps domains (kind of
   167  // like namespaces for Mapping labels) to arrays of label groups.
   168  type DomainMap map[string]MappingLabelGroupsArray
   169  
   170  // A MappingLabelGroupsArray is an array of MappingLabelGroups. I know, complex.
   171  type MappingLabelGroupsArray []MappingLabelGroup
   172  
   173  // A MappingLabelGroup is a single element of a MappingLabelGroupsArray: a second
   174  // map, where the key is a human-readable name that identifies the group.
   175  //
   176  // +kubebuilder:validation:MinProperties=1
   177  // +kubebuilder:validation:MaxProperties=1
   178  type MappingLabelGroup map[string]MappingLabelsArray
   179  
   180  // A MappingLabelsArray is the value in the MappingLabelGroup: an array of label
   181  // specifiers.
   182  type MappingLabelsArray []MappingLabelSpecifier
   183  
   184  // A MappingLabelSpecifier (finally!) defines a single label. There are multiple
   185  // kinds of label, so this is more complex than we'd like it to be. See the remarks
   186  // about schema on custom types in `./common.go`.
   187  //
   188  // +kubebuilder:validation:Type="d6e-union:string,object"
   189  type MappingLabelSpecifier struct {
   190  	String  *string                  `json:"-"` // source-cluster, destination-cluster, remote-address, or shorthand generic
   191  	Header  MappingLabelSpecHeader   `json:"-"` // header (NB: no need to make this a pointer because MappingLabelSpecHeader is already nil-able)
   192  	Generic *MappingLabelSpecGeneric `json:"-"` // longhand generic
   193  }
   194  
   195  // A MappingLabelSpecHeaderStruct is the value struct for MappingLabelSpecifier.Header:
   196  // the form of MappingLabelSpecifier to use when you want to take the label value from
   197  // an HTTP header. (If we make this an anonymous struct like the others, it breaks the
   198  // generation of its deepcopy routine. Sigh.)
   199  type MappingLabelSpecHeaderStruct struct {
   200  	Header           string `json:"header,omitifempty"`
   201  	OmitIfNotPresent *bool  `json:"omit_if_not_present,omitempty"`
   202  }
   203  
   204  // A MappingLabelSpecHeader is just the aggregate map of MappingLabelSpecHeaderStruct,
   205  // above. The key in the map is the label key that it will set to that header value;
   206  // there must be exactly one key in the map.
   207  type MappingLabelSpecHeader map[string]MappingLabelSpecHeaderStruct
   208  
   209  // func (in *MappingLabelSpecHeader) DeepCopyInfo(out *MappingLabelSpecHeader) {
   210  // 	x := in.OmitIfNotPresent
   211  
   212  // 	out = MappingLabelSpecHeader{
   213  // 		Header:           in.Header,
   214  // 		OmitIfNotPresent: &x,
   215  // 	}
   216  
   217  // 	return &out
   218  // }
   219  
   220  // A MappingLabelSpecGeneric is a longhand generic key: it states a string which
   221  // will be included literally in the label.
   222  type MappingLabelSpecGeneric struct {
   223  	GenericKey string `json:"generic_key"`
   224  	V3Key      string `json:"v3Key,omitempty"`
   225  }
   226  
   227  // MarshalJSON is important both so that we generate the proper
   228  // output, and to trigger controller-gen to not try to generate
   229  // jsonschema for our sub-fields:
   230  // https://github.com/kubernetes-sigs/controller-tools/pull/427
   231  func (o MappingLabelSpecifier) MarshalJSON() ([]byte, error) {
   232  	nonNil := 0
   233  	if o.String != nil {
   234  		nonNil++
   235  	}
   236  	if o.Header != nil {
   237  		nonNil++
   238  	}
   239  	if o.Generic != nil {
   240  		nonNil++
   241  	}
   242  	if nonNil > 1 {
   243  		return nil, errors.New("invalid MappingLabelSpecifier")
   244  	}
   245  
   246  	switch {
   247  	case o.String != nil:
   248  		return json.Marshal(o.String)
   249  	case o.Header != nil:
   250  		return json.Marshal(o.Header)
   251  	case o.Generic != nil:
   252  		return json.Marshal(o.Generic)
   253  	default:
   254  		return json.Marshal(nil)
   255  	}
   256  }
   257  
   258  // UnmarshalJSON is MarshalJSON's other half.
   259  func (o *MappingLabelSpecifier) UnmarshalJSON(data []byte) error {
   260  	// Handle "null" straight off...
   261  	if string(data) == "null" {
   262  		*o = MappingLabelSpecifier{}
   263  		return nil
   264  	}
   265  
   266  	// ...and if it's anything else, try all the possibilities in turn.
   267  	var err error
   268  
   269  	var header MappingLabelSpecHeader
   270  
   271  	if err = json.Unmarshal(data, &header); err == nil {
   272  		*o = MappingLabelSpecifier{Header: header}
   273  		return nil
   274  	}
   275  
   276  	var generic MappingLabelSpecGeneric
   277  
   278  	if err = json.Unmarshal(data, &generic); err == nil {
   279  		*o = MappingLabelSpecifier{Generic: &generic}
   280  		return nil
   281  	}
   282  
   283  	var str string
   284  
   285  	if err = json.Unmarshal(data, &str); err == nil {
   286  		*o = MappingLabelSpecifier{String: &str}
   287  		return nil
   288  	}
   289  
   290  	return errors.New("could not unmarshal MappingLabelSpecifier: invalid input")
   291  }
   292  
   293  // +kubebuilder:validation:Type="d6e-union:string,object"
   294  type AddedHeader struct {
   295  	Shorthand *string          `json:"-"`
   296  	Full      *AddedHeaderFull `json:"-"`
   297  }
   298  
   299  type AddedHeaderFull struct {
   300  	Value  string `json:"value,omitempty"`
   301  	Append *bool  `json:"append,omitempty"`
   302  }
   303  
   304  // MarshalJSON is important both so that we generate the proper
   305  // output, and to trigger controller-gen to not try to generate
   306  // jsonschema for our sub-fields:
   307  // https://github.com/kubernetes-sigs/controller-tools/pull/427
   308  func (o AddedHeader) MarshalJSON() ([]byte, error) {
   309  	switch {
   310  	case o.Shorthand != nil:
   311  		return json.Marshal(*o.Shorthand)
   312  	case o.Full != nil:
   313  		return json.Marshal(*o.Full)
   314  	default:
   315  		return json.Marshal(nil)
   316  	}
   317  }
   318  
   319  func (o *AddedHeader) UnmarshalJSON(data []byte) error {
   320  	if string(data) == "null" {
   321  		*o = AddedHeader{}
   322  		return nil
   323  	}
   324  
   325  	var err error
   326  
   327  	var str string
   328  	if err = json.Unmarshal(data, &str); err == nil {
   329  		*o = AddedHeader{Shorthand: &str}
   330  		return nil
   331  	}
   332  
   333  	var obj AddedHeaderFull
   334  	if err = json.Unmarshal(data, &obj); err == nil {
   335  		*o = AddedHeader{Full: &obj}
   336  		return nil
   337  	}
   338  
   339  	return err
   340  }
   341  
   342  type KeepAlive struct {
   343  	Probes   *int `json:"probes,omitempty"`
   344  	IdleTime *int `json:"idle_time,omitempty"`
   345  	Interval *int `json:"interval,omitempty"`
   346  }
   347  
   348  type CORS struct {
   349  	// +k8s:conversion-gen=false
   350  	Origins        *OriginList        `json:"origins,omitempty"`
   351  	Methods        StringOrStringList `json:"methods,omitempty"`
   352  	Headers        StringOrStringList `json:"headers,omitempty"`
   353  	Credentials    *bool              `json:"credentials,omitempty"`
   354  	ExposedHeaders StringOrStringList `json:"exposed_headers,omitempty"`
   355  	MaxAge         string             `json:"max_age,omitempty"`
   356  }
   357  
   358  // OriginList is a list of origin strings, either as a `[]string` or as a comma-separated `string`.
   359  //
   360  // +kubebuilder:validation:Type="d6e-union:string,array"
   361  type OriginList struct {
   362  	CommaSeparated bool     `json:"-"`
   363  	Values         []string `json:"-"`
   364  }
   365  
   366  func (l *OriginList) UnmarshalJSON(data []byte) error {
   367  	if string(data) == "null" {
   368  		*l = OriginList{}
   369  		return nil
   370  	}
   371  
   372  	var err error
   373  	var list []string
   374  	var single string
   375  
   376  	if err := json.Unmarshal(data, &single); err == nil {
   377  		*l = OriginList{
   378  			CommaSeparated: true,
   379  			Values:         strings.Split(single, ","),
   380  		}
   381  		return nil
   382  	}
   383  
   384  	if err = json.Unmarshal(data, &list); err == nil {
   385  		*l = OriginList{
   386  			Values: list,
   387  		}
   388  		return nil
   389  	}
   390  
   391  	return err
   392  }
   393  
   394  func (l OriginList) MarshalJSON() ([]byte, error) {
   395  	if l.CommaSeparated {
   396  		return json.Marshal(strings.Join(l.Values, ","))
   397  	}
   398  	return json.Marshal(l.Values)
   399  }
   400  
   401  type RetryPolicy struct {
   402  	// +kubebuilder:validation:Enum={"5xx","gateway-error","connect-failure","retriable-4xx","refused-stream","retriable-status-codes"}
   403  	RetryOn       string `json:"retry_on,omitempty"`
   404  	NumRetries    *int   `json:"num_retries,omitempty"`
   405  	PerTryTimeout string `json:"per_try_timeout,omitempty"`
   406  }
   407  
   408  type LoadBalancer struct {
   409  	// +kubebuilder:validation:Enum={"round_robin","ring_hash","maglev","least_request"}
   410  	// +kubebuilder:validation:Required
   411  	Policy   string              `json:"policy,omitempty"`
   412  	Cookie   *LoadBalancerCookie `json:"cookie,omitempty"`
   413  	Header   string              `json:"header,omitempty"`
   414  	SourceIp *bool               `json:"source_ip,omitempty"`
   415  }
   416  
   417  type LoadBalancerCookie struct {
   418  	// +kubebuilder:validation:Required
   419  	Name string `json:"name,omitempty"`
   420  	Path string `json:"path,omitempty"`
   421  	Ttl  string `json:"ttl,omitempty"`
   422  }
   423  
   424  // MappingStatus defines the observed state of Mapping
   425  type MappingStatus struct {
   426  	// +kubebuilder:validation:Enum={"","Inactive","Running"}
   427  	State string `json:"state,omitempty"`
   428  
   429  	Reason string `json:"reason,omitempty"`
   430  }
   431  
   432  // Mapping is the Schema for the mappings API
   433  //
   434  // +kubebuilder:object:root=true
   435  // +kubebuilder:storageversion
   436  // +kubebuilder:subresource:status
   437  // +kubebuilder:printcolumn:name="Source Host",type=string,JSONPath=`.spec.host`
   438  // +kubebuilder:printcolumn:name="Source Prefix",type=string,JSONPath=`.spec.prefix`
   439  // +kubebuilder:printcolumn:name="Dest Service",type=string,JSONPath=`.spec.service`
   440  // +kubebuilder:printcolumn:name="State",type=string,JSONPath=`.status.state`
   441  // +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.reason`
   442  type Mapping struct {
   443  	metav1.TypeMeta   `json:""`
   444  	metav1.ObjectMeta `json:"metadata,omitempty"`
   445  
   446  	Spec   MappingSpec    `json:"spec,omitempty"`
   447  	Status *MappingStatus `json:"status,omitempty"`
   448  }
   449  
   450  // MappingList contains a list of Mappings.
   451  //
   452  // +kubebuilder:object:root=true
   453  type MappingList struct {
   454  	metav1.TypeMeta `json:""`
   455  	metav1.ListMeta `json:"metadata,omitempty"`
   456  	Items           []Mapping `json:"items"`
   457  }
   458  
   459  func init() {
   460  	SchemeBuilder.Register(&Mapping{}, &MappingList{})
   461  }
   462  

View as plain text