...

Source file src/sigs.k8s.io/gateway-api/pkg/test/cel/httproute_experimental_test.go

Documentation: sigs.k8s.io/gateway-api/pkg/test/cel

     1  //go:build experimental
     2  // +build experimental
     3  
     4  /*
     5  Copyright 2023 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package main
    21  
    22  import (
    23  	"fmt"
    24  	"testing"
    25  	"time"
    26  
    27  	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
    28  
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  )
    31  
    32  ////////////////////////////////////////////////////////////////////////////////
    33  ////////////////////////////////////////////////////////////////////////////////
    34  //
    35  // How are tests named? Where to add new tests?
    36  //
    37  // Ensure that tests for newly added CEL validations are added in the correctly
    38  // named test function. For example, if you added a test at the
    39  // `HTTPRouteFilter` hierarchy (i.e. either at the struct level, or on one of
    40  // the immediate descendent fields), then the test will go in the
    41  // TestHTTPRouteFilter function. If the appropriate test function does not
    42  // exist, please create one.
    43  //
    44  ////////////////////////////////////////////////////////////////////////////////
    45  ////////////////////////////////////////////////////////////////////////////////
    46  
    47  func TestHTTPRouteParentRefExperimental(t *testing.T) {
    48  	tests := []struct {
    49  		name       string
    50  		wantErrors []string
    51  		parentRefs []gatewayv1.ParentReference
    52  	}{
    53  		{
    54  			name:       "invalid because duplicate parent refs without port or section name",
    55  			wantErrors: []string{"sectionName or port must be unique when parentRefs includes 2 or more references to the same parent"},
    56  			parentRefs: []gatewayv1.ParentReference{{
    57  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
    58  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
    59  				Name:  "example",
    60  			}, {
    61  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
    62  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
    63  				Name:  "example",
    64  			}},
    65  		},
    66  		{
    67  			name:       "invalid because duplicate parent refs with only one port",
    68  			wantErrors: []string{"sectionName or port must be specified when parentRefs includes 2 or more references to the same parent"},
    69  			parentRefs: []gatewayv1.ParentReference{{
    70  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
    71  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
    72  				Name:  "example",
    73  			}, {
    74  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
    75  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
    76  				Name:  "example",
    77  				Port:  ptrTo(gatewayv1.PortNumber(80)),
    78  			}},
    79  		},
    80  		{
    81  			name:       "invalid because duplicate parent refs with only one sectionName and port",
    82  			wantErrors: []string{"sectionName or port must be specified when parentRefs includes 2 or more references to the same parent"},
    83  			parentRefs: []gatewayv1.ParentReference{{
    84  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
    85  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
    86  				Name:  "example",
    87  			}, {
    88  				Kind:        ptrTo(gatewayv1.Kind("Gateway")),
    89  				Group:       ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
    90  				Name:        "example",
    91  				SectionName: ptrTo(gatewayv1.SectionName("foo")),
    92  				Port:        ptrTo(gatewayv1.PortNumber(80)),
    93  			}},
    94  		},
    95  		{
    96  			name:       "invalid because duplicate parent refs with duplicate ports",
    97  			wantErrors: []string{"sectionName or port must be unique when parentRefs includes 2 or more references to the same parent"},
    98  			parentRefs: []gatewayv1.ParentReference{{
    99  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
   100  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   101  				Name:  "example",
   102  				Port:  ptrTo(gatewayv1.PortNumber(80)),
   103  			}, {
   104  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
   105  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   106  				Name:  "example",
   107  				Port:  ptrTo(gatewayv1.PortNumber(80)),
   108  			}},
   109  		},
   110  		{
   111  			name:       "valid single parentRef without sectionName or port",
   112  			wantErrors: []string{},
   113  			parentRefs: []gatewayv1.ParentReference{{
   114  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
   115  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   116  				Name:  "example",
   117  			}},
   118  		},
   119  		{
   120  			name:       "valid single parentRef with sectionName and port",
   121  			wantErrors: []string{},
   122  			parentRefs: []gatewayv1.ParentReference{{
   123  				Kind:        ptrTo(gatewayv1.Kind("Gateway")),
   124  				Group:       ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   125  				Name:        "example",
   126  				SectionName: ptrTo(gatewayv1.SectionName("foo")),
   127  				Port:        ptrTo(gatewayv1.PortNumber(443)),
   128  			}},
   129  		},
   130  		{
   131  			name:       "valid because duplicate parent refs with different ports",
   132  			wantErrors: []string{},
   133  			parentRefs: []gatewayv1.ParentReference{{
   134  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
   135  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   136  				Name:  "example",
   137  				Port:  ptrTo(gatewayv1.PortNumber(80)),
   138  			}, {
   139  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
   140  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   141  				Name:  "example",
   142  				Port:  ptrTo(gatewayv1.PortNumber(443)),
   143  			}},
   144  		},
   145  		{
   146  			name:       "invalid ParentRefs with multiple mixed references to the same parent",
   147  			wantErrors: []string{"sectionName or port must be specified when parentRefs includes 2 or more references to the same parent"},
   148  			parentRefs: []gatewayv1.ParentReference{{
   149  				Kind:        ptrTo(gatewayv1.Kind("Gateway")),
   150  				Group:       ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   151  				Name:        "example",
   152  				SectionName: ptrTo(gatewayv1.SectionName("foo")),
   153  			}, {
   154  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
   155  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   156  				Name:  "example",
   157  				Port:  ptrTo(gatewayv1.PortNumber(443)),
   158  			}},
   159  		},
   160  		{
   161  			name:       "valid ParentRefs with multiple same port references to different section of a parent",
   162  			wantErrors: []string{},
   163  			parentRefs: []gatewayv1.ParentReference{{
   164  				Name:        "example",
   165  				Port:        ptrTo(gatewayv1.PortNumber(443)),
   166  				SectionName: ptrTo(gatewayv1.SectionName("foo")),
   167  			}, {
   168  				Name:        "example",
   169  				Port:        ptrTo(gatewayv1.PortNumber(443)),
   170  				SectionName: ptrTo(gatewayv1.SectionName("bar")),
   171  			}},
   172  		},
   173  		{
   174  			// when referencing the same object, both parentRefs need to specify
   175  			// the same optional fields (both parentRefs must specify port,
   176  			// sectionName, or both)
   177  			name:       "invalid because duplicate parent refs with first having sectionName and second having both sectionName and port",
   178  			wantErrors: []string{"sectionName or port must be specified when parentRefs includes 2 or more references to the same parent"},
   179  			parentRefs: []gatewayv1.ParentReference{{
   180  				Kind:        ptrTo(gatewayv1.Kind("Gateway")),
   181  				Group:       ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   182  				Name:        "example",
   183  				SectionName: ptrTo(gatewayv1.SectionName("foo")),
   184  			}, {
   185  				Kind:        ptrTo(gatewayv1.Kind("Gateway")),
   186  				Group:       ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   187  				Name:        "example",
   188  				Port:        ptrTo(gatewayv1.PortNumber(443)),
   189  				SectionName: ptrTo(gatewayv1.SectionName("foo")),
   190  			}},
   191  		},
   192  		{
   193  			name:       "valid because first parentRef has namespace while second doesn't",
   194  			wantErrors: []string{},
   195  			parentRefs: []gatewayv1.ParentReference{{
   196  				Kind:      ptrTo(gatewayv1.Kind("Gateway")),
   197  				Group:     ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   198  				Name:      "example",
   199  				Namespace: ptrTo(gatewayv1.Namespace("test")),
   200  			}, {
   201  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
   202  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   203  				Name:  "example",
   204  			}},
   205  		},
   206  		{
   207  			name:       "valid because second parentRef has namespace while first doesn't",
   208  			wantErrors: []string{},
   209  			parentRefs: []gatewayv1.ParentReference{{
   210  				Kind:  ptrTo(gatewayv1.Kind("Gateway")),
   211  				Group: ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   212  				Name:  "example",
   213  			}, {
   214  				Kind:      ptrTo(gatewayv1.Kind("Gateway")),
   215  				Group:     ptrTo(gatewayv1.Group("gateway.networking.k8s.io")),
   216  				Name:      "example",
   217  				Namespace: ptrTo(gatewayv1.Namespace("test")),
   218  			}},
   219  		},
   220  	}
   221  
   222  	for _, tc := range tests {
   223  		t.Run(tc.name, func(t *testing.T) {
   224  			route := &gatewayv1.HTTPRoute{
   225  				ObjectMeta: metav1.ObjectMeta{
   226  					Name:      fmt.Sprintf("foo-%v", time.Now().UnixNano()),
   227  					Namespace: metav1.NamespaceDefault,
   228  				},
   229  				Spec: gatewayv1.HTTPRouteSpec{
   230  					CommonRouteSpec: gatewayv1.CommonRouteSpec{
   231  						ParentRefs: tc.parentRefs,
   232  					},
   233  				},
   234  			}
   235  			validateHTTPRoute(t, route, tc.wantErrors)
   236  		})
   237  	}
   238  }
   239  
   240  func toDuration(durationString string) *gatewayv1.Duration {
   241  	return (*gatewayv1.Duration)(&durationString)
   242  }
   243  
   244  func TestHTTPRouteTimeouts(t *testing.T) {
   245  	tests := []struct {
   246  		name       string
   247  		wantErrors []string
   248  		rules      []gatewayv1.HTTPRouteRule
   249  	}{
   250  		{
   251  			name:       "invalid timeout unit us is not supported",
   252  			wantErrors: []string{"Invalid value: \"100us\": spec.rules[0].timeouts.request in body should match '^([0-9]{1,5}(h|m|s|ms)){1,4}$'"},
   253  			rules: []gatewayv1.HTTPRouteRule{
   254  				{
   255  					Timeouts: &gatewayv1.HTTPRouteTimeouts{
   256  						Request: toDuration("100us"),
   257  					},
   258  				},
   259  			},
   260  		},
   261  		{
   262  			name:       "invalid timeout unit ns is not supported",
   263  			wantErrors: []string{"Invalid value: \"500ns\": spec.rules[0].timeouts.request in body should match '^([0-9]{1,5}(h|m|s|ms)){1,4}$'"},
   264  			rules: []gatewayv1.HTTPRouteRule{
   265  				{
   266  					Timeouts: &gatewayv1.HTTPRouteTimeouts{
   267  						Request: toDuration("500ns"),
   268  					},
   269  				},
   270  			},
   271  		},
   272  		{
   273  			name: "valid timeout request and backendRequest",
   274  			rules: []gatewayv1.HTTPRouteRule{
   275  				{
   276  					Timeouts: &gatewayv1.HTTPRouteTimeouts{
   277  						Request:        toDuration("4s"),
   278  						BackendRequest: toDuration("2s"),
   279  					},
   280  				},
   281  			},
   282  		},
   283  		{
   284  			name: "valid timeout request",
   285  			rules: []gatewayv1.HTTPRouteRule{
   286  				{
   287  					Timeouts: &gatewayv1.HTTPRouteTimeouts{
   288  						Request: toDuration("0s"),
   289  					},
   290  				},
   291  			},
   292  		},
   293  		{
   294  			name:       "invalid timeout request day unit not supported",
   295  			wantErrors: []string{"Invalid value: \"1d\": spec.rules[0].timeouts.request in body should match '^([0-9]{1,5}(h|m|s|ms)){1,4}$'"},
   296  			rules: []gatewayv1.HTTPRouteRule{
   297  				{
   298  					Timeouts: &gatewayv1.HTTPRouteTimeouts{
   299  						Request: toDuration("1d"),
   300  					},
   301  				},
   302  			},
   303  		},
   304  		{
   305  			name:       "invalid timeout request decimal not supported ",
   306  			wantErrors: []string{"Invalid value: \"0.5s\": spec.rules[0].timeouts.request in body should match '^([0-9]{1,5}(h|m|s|ms)){1,4}$'"},
   307  			rules: []gatewayv1.HTTPRouteRule{
   308  				{
   309  					Timeouts: &gatewayv1.HTTPRouteTimeouts{
   310  						Request: toDuration("0.5s"),
   311  					},
   312  				},
   313  			},
   314  		},
   315  		{
   316  			name: "valid timeout request infinite greater than backendRequest 1ms",
   317  			rules: []gatewayv1.HTTPRouteRule{
   318  				{
   319  					Timeouts: &gatewayv1.HTTPRouteTimeouts{
   320  						Request:        toDuration("0s"),
   321  						BackendRequest: toDuration("1ms"),
   322  					},
   323  				},
   324  			},
   325  		},
   326  		{
   327  			name: "valid timeout request 1s greater than backendRequest 200ms",
   328  			rules: []gatewayv1.HTTPRouteRule{
   329  				{
   330  					Timeouts: &gatewayv1.HTTPRouteTimeouts{
   331  						Request:        toDuration("1s"),
   332  						BackendRequest: toDuration("200ms"),
   333  					},
   334  				},
   335  			},
   336  		},
   337  		{
   338  			name: "valid timeout request 10s equal backendRequest 10s",
   339  			rules: []gatewayv1.HTTPRouteRule{
   340  				{
   341  					Timeouts: &gatewayv1.HTTPRouteTimeouts{
   342  						Request:        toDuration("10s"),
   343  						BackendRequest: toDuration("10s"),
   344  					},
   345  				},
   346  			},
   347  		},
   348  		{
   349  			name:       "invalid timeout request 200ms less than backendRequest 1s",
   350  			wantErrors: []string{"Invalid value: \"object\": backendRequest timeout cannot be longer than request timeout"},
   351  			rules: []gatewayv1.HTTPRouteRule{
   352  				{
   353  					Timeouts: &gatewayv1.HTTPRouteTimeouts{
   354  						Request:        toDuration("200ms"),
   355  						BackendRequest: toDuration("1s"),
   356  					},
   357  				},
   358  			},
   359  		},
   360  	}
   361  
   362  	for _, tc := range tests {
   363  		t.Run(tc.name, func(t *testing.T) {
   364  			route := &gatewayv1.HTTPRoute{
   365  				ObjectMeta: metav1.ObjectMeta{
   366  					Name:      fmt.Sprintf("foo-%v", time.Now().UnixNano()),
   367  					Namespace: metav1.NamespaceDefault,
   368  				},
   369  				Spec: gatewayv1.HTTPRouteSpec{Rules: tc.rules},
   370  			}
   371  			validateHTTPRoute(t, route, tc.wantErrors)
   372  		})
   373  	}
   374  }
   375  

View as plain text