diff --git a/.github/workflows/conformance-test.yml b/.github/workflows/conformance-test.yml index fffe5d763..3a7ae9c4f 100644 --- a/.github/workflows/conformance-test.yml +++ b/.github/workflows/conformance-test.yml @@ -99,6 +99,12 @@ jobs: continue-on-error: true run: | make conformance-test + + - name: Get Logs from api7-ingress-controller + shell: bash + run: | + export KUBECONFIG=/tmp/api7-ingress-cluster.kubeconfig + kubectl logs -n api7ee-conformance-test -l app=api7-ingress-controller - name: Upload Gateway API Conformance Report if: ${{ github.event_name == 'push' }} diff --git a/internal/provider/adc/cache/cache.go b/internal/provider/adc/cache/cache.go index b0fe6a6e1..edfbf4ad9 100644 --- a/internal/provider/adc/cache/cache.go +++ b/internal/provider/adc/cache/cache.go @@ -1,9 +1,6 @@ -// Licensed to the Apache Software Foundation (ASF) under one or more -// contributor license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright ownership. -// The ASF licenses this file to You under the Apache License, Version 2.0 -// (the "License"); you may not use this file except in compliance with -// the License. You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // diff --git a/internal/provider/adc/cache/memdb.go b/internal/provider/adc/cache/memdb.go index 1293f193d..50e22c983 100644 --- a/internal/provider/adc/cache/memdb.go +++ b/internal/provider/adc/cache/memdb.go @@ -1,18 +1,3 @@ -// Licensed to the Apache Software Foundation (ASF) under one or more -// contributor license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright ownership. -// The ASF licenses this file to You under the Apache License, Version 2.0 -// (the "License"); you may not use this file except in compliance with -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package cache import ( diff --git a/internal/provider/adc/cache/schema.go b/internal/provider/adc/cache/schema.go index c061057d5..f43d08c2d 100644 --- a/internal/provider/adc/cache/schema.go +++ b/internal/provider/adc/cache/schema.go @@ -1,18 +1,3 @@ -// Licensed to the Apache Software Foundation (ASF) under one or more -// contributor license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright ownership. -// The ASF licenses this file to You under the Apache License, Version 2.0 -// (the "License"); you may not use this file except in compliance with -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package cache import ( diff --git a/internal/provider/adc/store.go b/internal/provider/adc/store.go index 88da5bfe3..05a2a61d5 100644 --- a/internal/provider/adc/store.go +++ b/internal/provider/adc/store.go @@ -151,10 +151,11 @@ func (s *Store) Delete(name string, resourceTypes []string, Labels map[string]st delete(s.globalruleMap, name) case "plugin_metadata": delete(s.pluginMetadataMap, name) - default: - delete(s.cacheMap, name) } } + if len(resourceTypes) == 0 { + delete(s.cacheMap, name) + } return nil } diff --git a/internal/provider/adc/translator/httproute.go b/internal/provider/adc/translator/httproute.go index a51b60139..bef514332 100644 --- a/internal/provider/adc/translator/httproute.go +++ b/internal/provider/adc/translator/httproute.go @@ -15,7 +15,6 @@ package translator import ( "encoding/json" "fmt" - "sort" "strings" "github.com/api7/gopkg/pkg/log" @@ -354,31 +353,61 @@ func (t *Translator) translateBackendRef(tctx *provider.TranslateContext, ref ga return t.translateEndpointSlice(portName, weight, endpointSlices) } -// calculateMatchPriority calculate the priority of HTTPRouteMatch, according to the Gateway API specification -// the higher the return value, the higher the priority -func calculateMatchPriority(match *gatewayv1.HTTPRouteMatch) int64 { - var score int64 = 0 +// calculateHTTPRoutePriority calculates the priority of the HTTP route. +// ref: https://github.com/Kong/kubernetes-ingress-controller/blob/57472721319e2c63e56cb8540425257e8e02520f/internal/dataplane/translator/subtranslator/httproute_atc.go#L279-L296 +func calculateHTTPRoutePriority(match *gatewayv1.HTTPRouteMatch, ruleIndex int) uint64 { + const ( + // ExactPathShiftBits assigns bit 34 to mark if the match is exact path match. + ExactPathShiftBits = 34 + // PathLengthShiftBits assigns bits 23-32 to path length. (max length = 1024, but must start with /) + PathLengthShiftBits = 23 + // MethodMatchShiftBits assigns bit 22 to mark if method is specified. + MethodMatchShiftBits = 22 + // HeaderNumberShiftBits assign bits 17-21 to number of headers. (max number of headers = 16) + HeaderNumberShiftBits = 17 + // QueryParamNumberShiftBits makes bits 12-16 used for number of query params (max number of query params = 16) + QueryParamNumberShiftBits = 12 + // RuleIndexShiftBits assigns bits 7-11 to rule index. (max number of rules = 16) + RuleIndexShiftBits = 7 + ) + + var priority uint64 = 0 - // 1. Exact path matches have the highest priority + // ExactPathShiftBits if match.Path != nil && match.Path.Type != nil && *match.Path.Type == gatewayv1.PathMatchExact { - score += 10000 - } else if match.Path != nil && match.Path.Type != nil && *match.Path.Type == gatewayv1.PathMatchPathPrefix && match.Path.Value != nil { - // 2. Prefix path matches, the longer the string, the higher the priority - score += 1000 + int64(len(*match.Path.Value)) + priority |= (1 << ExactPathShiftBits) + } + + // PathLengthShiftBits + // max length of path is 1024, but path must start with /, so we use PathLength-1 to fill the bits. + if match.Path != nil && match.Path.Value != nil { + pathLength := len(*match.Path.Value) + if pathLength > 0 { + priority |= (uint64(pathLength-1) << PathLengthShiftBits) + } } - // 3. Method matching + // MethodMatchShiftBits if match.Method != nil { - score += 100 + priority |= (1 << MethodMatchShiftBits) } - // 4. Header matching, the more headers, the higher the priority - score += int64(len(match.Headers) * 10) + // HeaderNumberShiftBits + headerCount := len(match.Headers) + priority |= (uint64(headerCount) << HeaderNumberShiftBits) - // 5. Query parameter matching, the more query parameters, the higher the priority - score += int64(len(match.QueryParams)) + // QueryParamNumberShiftBits + queryParamCount := len(match.QueryParams) + priority |= (uint64(queryParamCount) << QueryParamNumberShiftBits) - return score + // RuleIndexShiftBits + index := 16 - ruleIndex + if index < 0 { + index = 0 + } + priority |= (uint64(index) << RuleIndexShiftBits) + + return priority } func (t *Translator) TranslateHTTPRoute(tctx *provider.TranslateContext, httpRoute *gatewayv1.HTTPRoute) (*TranslateResult, error) { @@ -393,7 +422,7 @@ func (t *Translator) TranslateHTTPRoute(tctx *provider.TranslateContext, httpRou labels := label.GenLabel(httpRoute) - for i, rule := range rules { + for ruleIndex, rule := range rules { upstream := adctypes.NewDefaultUpstream() for _, backend := range rule.BackendRefs { if backend.Namespace == nil { @@ -410,7 +439,7 @@ func (t *Translator) TranslateHTTPRoute(tctx *provider.TranslateContext, httpRou service := adctypes.NewDefaultService() service.Labels = labels - service.Name = adctypes.ComposeServiceNameWithRule(httpRoute.Namespace, httpRoute.Name, fmt.Sprintf("%d", i)) + service.Name = adctypes.ComposeServiceNameWithRule(httpRoute.Namespace, httpRoute.Name, fmt.Sprintf("%d", ruleIndex)) service.ID = id.GenID(service.Name) service.Hosts = hosts service.Upstream = upstream @@ -428,11 +457,6 @@ func (t *Translator) TranslateHTTPRoute(tctx *provider.TranslateContext, httpRou }, }, } - } else { - // Sort the matches by priority - sort.Slice(matches, func(a, b int) bool { - return calculateMatchPriority(&matches[a]) > calculateMatchPriority(&matches[b]) - }) } routes := []*adctypes.Route{} @@ -442,15 +466,15 @@ func (t *Translator) TranslateHTTPRoute(tctx *provider.TranslateContext, httpRou return nil, err } - name := adctypes.ComposeRouteName(httpRoute.Namespace, httpRoute.Name, fmt.Sprintf("%d-%d", i, j)) + name := adctypes.ComposeRouteName(httpRoute.Namespace, httpRoute.Name, fmt.Sprintf("%d-%d", ruleIndex, j)) route.Name = name route.ID = id.GenID(name) route.Labels = labels route.EnableWebsocket = ptr.To(true) // Set the route priority - priority := calculateMatchPriority(&match) - route.Priority = &priority + priority := calculateHTTPRoutePriority(&match, ruleIndex) + route.Priority = ptr.To(int64(priority)) routes = append(routes, route) } diff --git a/test/conformance/suite_test.go b/test/conformance/suite_test.go index 0bf5e876e..18bb3b132 100644 --- a/test/conformance/suite_test.go +++ b/test/conformance/suite_test.go @@ -1,3 +1,14 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package conformance import ( @@ -149,6 +160,7 @@ func TestMain(m *testing.M) { Namespace: namespace, AdminEnpoint: framework.DashboardTLSEndpoint, StatusAddress: address, + InitSyncDelay: 30 * time.Minute, }) defaultGatewayProxyOpts = GatewayProxyOpts{ diff --git a/test/e2e/framework/ingress.go b/test/e2e/framework/ingress.go index 2a8bdae38..34555bb0c 100644 --- a/test/e2e/framework/ingress.go +++ b/test/e2e/framework/ingress.go @@ -46,6 +46,7 @@ type IngressDeployOpts struct { AdminEnpoint string StatusAddress string Replicas int + InitSyncDelay time.Duration } func (f *Framework) DeployIngress(opts IngressDeployOpts) { diff --git a/test/e2e/framework/manifests/ingress.yaml b/test/e2e/framework/manifests/ingress.yaml index 644d609e1..925568a40 100644 --- a/test/e2e/framework/manifests/ingress.yaml +++ b/test/e2e/framework/manifests/ingress.yaml @@ -345,7 +345,7 @@ data: # The period between two consecutive syncs. # The default value is 0 seconds, which means the controller will not sync. # If you want to enable the sync, set it to a positive value. - init_sync_delay: 1m + init_sync_delay: {{ .InitSyncDelay | default "1m" }} --- apiVersion: v1 kind: Service