Skip to content

Commit 6e87cc6

Browse files
authored
feat: implement reconciler for apisixroute (#164)
1 parent 0ca0a72 commit 6e87cc6

36 files changed

+1269
-302
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ GATEAY_API_VERSION ?= v1.2.0
1414
DASHBOARD_VERSION ?= dev
1515
ADC_VERSION ?= 0.19.0
1616

17-
TEST_TIMEOUT ?= 60m
17+
TEST_TIMEOUT ?= 80m
1818
TEST_DIR ?= ./test/e2e/
1919

2020
# CRD Reference Documentation

api/v2/apisixpluginconfig_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type ApisixPluginConfigSpec struct {
2828
}
2929

3030
// ApisixPluginConfigStatus defines the observed state of ApisixPluginConfig.
31-
type ApisixPluginConfigStatus ApisixStatus
31+
type ApisixPluginConfigStatus = ApisixStatus
3232

3333
// +kubebuilder:object:root=true
3434
// +kubebuilder:subresource:status

api/v2/apisixroute_types.go

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@
1313
package v2
1414

1515
import (
16+
"strings"
17+
18+
"github.com/pkg/errors"
1619
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1720
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1821
"k8s.io/apimachinery/pkg/util/intstr"
22+
23+
"github.com/apache/apisix-ingress-controller/api/adc"
1924
)
2025

2126
// ApisixRouteSpec is the spec definition for ApisixRouteSpec.
@@ -66,6 +71,7 @@ type ApisixRouteHTTP struct {
6671
// Upstreams refer to ApisixUpstream CRD
6772
Upstreams []ApisixRouteUpstreamReference `json:"upstreams,omitempty" yaml:"upstreams,omitempty"`
6873

74+
// +kubebuilder:validation:Optional
6975
Websocket bool `json:"websocket" yaml:"websocket"`
7076
PluginConfigName string `json:"plugin_config_name,omitempty" yaml:"plugin_config_name,omitempty"`
7177
// By default, PluginConfigNamespace will be the same as the namespace of ApisixRoute
@@ -118,7 +124,7 @@ type ApisixRouteHTTPMatch struct {
118124
// value:
119125
// - "127.0.0.1"
120126
// - "10.0.5.11"
121-
NginxVars []ApisixRouteHTTPMatchExpr `json:"exprs,omitempty" yaml:"exprs,omitempty"`
127+
NginxVars ApisixRouteHTTPMatchExprs `json:"exprs,omitempty" yaml:"exprs,omitempty"`
122128
// Matches based on a user-defined filtering function.
123129
// These functions can accept an input parameter `vars`
124130
// which can be used to access the Nginx variables.
@@ -153,6 +159,7 @@ type ApisixRouteHTTPBackend struct {
153159
// default is endpoints.
154160
ResolveGranularity string `json:"resolveGranularity,omitempty" yaml:"resolveGranularity,omitempty"`
155161
// Weight of this backend.
162+
// +kubebuilder:validation:Optional
156163
Weight *int `json:"weight" yaml:"weight"`
157164
// Subset specifies a subset for the target Service. The subset should be pre-defined
158165
// in ApisixUpstream about this service.
@@ -211,14 +218,107 @@ type ApisixRouteHTTPMatchExpr struct {
211218
Op string `json:"op" yaml:"op"`
212219
// Set is an array type object of the expression.
213220
// It should be used when the Op is "in" or "not_in";
221+
// +kubebuilder:validation:Optional
214222
Set []string `json:"set" yaml:"set"`
215223
// Value is the normal type object for the expression,
216224
// it should be used when the Op is not "in" and "not_in".
217225
// Set and Value are exclusive so only of them can be set
218226
// in the same time.
227+
// +kubebuilder:validation:Optional
219228
Value *string `json:"value" yaml:"value"`
220229
}
221230

231+
type ApisixRouteHTTPMatchExprs []ApisixRouteHTTPMatchExpr
232+
233+
func (exprs ApisixRouteHTTPMatchExprs) ToVars() (result adc.Vars, err error) {
234+
for _, expr := range exprs {
235+
if expr.Subject.Name == "" && expr.Subject.Scope != ScopePath {
236+
return result, errors.New("empty subject.name")
237+
}
238+
239+
// process key
240+
var (
241+
subj string
242+
this adc.StringOrSlice
243+
)
244+
switch expr.Subject.Scope {
245+
case ScopeQuery:
246+
subj = "arg_" + expr.Subject.Name
247+
case ScopeHeader:
248+
subj = "http_" + strings.ReplaceAll(strings.ToLower(expr.Subject.Name), "-", "_")
249+
case ScopeCookie:
250+
subj = "cookie_" + expr.Subject.Name
251+
case ScopePath:
252+
subj = "uri"
253+
case ScopeVariable:
254+
subj = expr.Subject.Name
255+
default:
256+
return result, errors.New("invalid http match expr: subject.scope should be one of [query, header, cookie, path, variable]")
257+
}
258+
this.SliceVal = append(this.SliceVal, adc.StringOrSlice{StrVal: subj})
259+
260+
// process operator
261+
var (
262+
op string
263+
)
264+
switch expr.Op {
265+
case OpEqual:
266+
op = "=="
267+
case OpGreaterThan:
268+
op = ">"
269+
case OpGreaterThanEqual:
270+
op = ">="
271+
case OpIn:
272+
op = "in"
273+
case OpLessThan:
274+
op = "<"
275+
case OpLessThanEqual:
276+
op = "<="
277+
case OpNotEqual:
278+
op = "~="
279+
case OpNotIn:
280+
op = "in"
281+
case OpRegexMatch:
282+
op = "~~"
283+
case OpRegexMatchCaseInsensitive:
284+
op = "~*"
285+
case OpRegexNotMatch:
286+
op = "~~"
287+
case OpRegexNotMatchCaseInsensitive:
288+
op = "~*"
289+
default:
290+
return result, errors.New("unknown operator")
291+
}
292+
if expr.Op == OpNotIn || expr.Op == OpRegexNotMatch || expr.Op == OpRegexNotMatchCaseInsensitive {
293+
this.SliceVal = append(this.SliceVal, adc.StringOrSlice{StrVal: "!"})
294+
}
295+
this.SliceVal = append(this.SliceVal, adc.StringOrSlice{StrVal: op})
296+
297+
// process value
298+
switch expr.Op {
299+
case OpIn, OpNotIn:
300+
if expr.Set == nil {
301+
return result, errors.New("empty set value")
302+
}
303+
var value adc.StringOrSlice
304+
for _, item := range expr.Set {
305+
value.SliceVal = append(value.SliceVal, adc.StringOrSlice{StrVal: item})
306+
}
307+
this.SliceVal = append(this.SliceVal, value)
308+
default:
309+
if expr.Value == nil {
310+
return result, errors.New("empty value")
311+
}
312+
this.SliceVal = append(this.SliceVal, adc.StringOrSlice{StrVal: *expr.Value})
313+
}
314+
315+
// append to result
316+
result = append(result, this.SliceVal)
317+
}
318+
319+
return result, nil
320+
}
321+
222322
// ApisixRoutePluginConfig is the configuration for
223323
// any plugins.
224324
type ApisixRoutePluginConfig map[string]apiextensionsv1.JSON

api/v2/groupversion_info.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package v2
1717

1818
import (
1919
"k8s.io/apimachinery/pkg/runtime/schema"
20+
"sigs.k8s.io/controller-runtime/pkg/client"
2021
"sigs.k8s.io/controller-runtime/pkg/scheme"
2122
)
2223

@@ -30,3 +31,12 @@ var (
3031
// AddToScheme adds the types in this group-version to the given scheme.
3132
AddToScheme = SchemeBuilder.AddToScheme
3233
)
34+
35+
func Is(obj client.Object) bool {
36+
switch obj.(type) {
37+
case *ApisixConsumer, *ApisixGlobalRule, *ApisixPluginConfig, *ApisixRoute, *ApisixTls, *ApisixUpstream:
38+
return obj.GetObjectKind().GroupVersionKind().GroupVersion() == GroupVersion
39+
default:
40+
return false
41+
}
42+
}

api/v2/reason.go

Lines changed: 0 additions & 19 deletions
This file was deleted.

api/v2/shared_types.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
package v2
14+
15+
import (
16+
"time"
17+
18+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
19+
)
20+
21+
type (
22+
// ApisixRouteConditionType is a type of condition for a route.
23+
ApisixRouteConditionType = gatewayv1.RouteConditionType
24+
// ApisixRouteConditionReason is a reason for a route condition.
25+
ApisixRouteConditionReason = gatewayv1.RouteConditionReason
26+
)
27+
28+
const (
29+
ConditionTypeAccepted ApisixRouteConditionType = gatewayv1.RouteConditionAccepted
30+
ConditionReasonAccepted ApisixRouteConditionReason = gatewayv1.RouteReasonAccepted
31+
ConditionReasonInvalidSpec ApisixRouteConditionReason = "InvalidSpec"
32+
ConditionReasonSyncFailed ApisixRouteConditionReason = "SyncFailed"
33+
)
34+
35+
const (
36+
// DefaultUpstreamTimeout represents the default connect,
37+
// read and send timeout (in seconds) with upstreams.
38+
DefaultUpstreamTimeout = 60 * time.Second
39+
40+
DefaultWeight = 100
41+
)
42+
43+
const (
44+
// OpEqual means the equal ("==") operator in nginxVars.
45+
OpEqual = "Equal"
46+
// OpNotEqual means the not equal ("~=") operator in nginxVars.
47+
OpNotEqual = "NotEqual"
48+
// OpGreaterThan means the greater than (">") operator in nginxVars.
49+
OpGreaterThan = "GreaterThan"
50+
// OpGreaterThanEqual means the greater than (">=") operator in nginxVars.
51+
OpGreaterThanEqual = "GreaterThanEqual"
52+
// OpLessThan means the less than ("<") operator in nginxVars.
53+
OpLessThan = "LessThan"
54+
// OpLessThanEqual means the less than equal ("<=") operator in nginxVars.
55+
OpLessThanEqual = "LessThanEqual"
56+
// OpRegexMatch means the regex match ("~~") operator in nginxVars.
57+
OpRegexMatch = "RegexMatch"
58+
// OpRegexNotMatch means the regex not match ("!~~") operator in nginxVars.
59+
OpRegexNotMatch = "RegexNotMatch"
60+
// OpRegexMatchCaseInsensitive means the regex match "~*" (case insensitive mode) operator in nginxVars.
61+
OpRegexMatchCaseInsensitive = "RegexMatchCaseInsensitive"
62+
// OpRegexNotMatchCaseInsensitive means the regex not match "!~*" (case insensitive mode) operator in nginxVars.
63+
OpRegexNotMatchCaseInsensitive = "RegexNotMatchCaseInsensitive"
64+
// OpIn means the in operator ("in") in nginxVars.
65+
OpIn = "In"
66+
// OpNotIn means the not in operator ("not_in") in nginxVars.
67+
OpNotIn = "NotIn"
68+
69+
// ScopeQuery means the route match expression subject is in the querystring.
70+
ScopeQuery = "Query"
71+
// ScopeHeader means the route match expression subject is in request headers.
72+
ScopeHeader = "Header"
73+
// ScopePath means the route match expression subject is the uri path.
74+
ScopePath = "Path"
75+
// ScopeCookie means the route match expression subject is in cookie.
76+
ScopeCookie = "Cookie"
77+
// ScopeVariable means the route match expression subject is in variable.
78+
ScopeVariable = "Variable"
79+
)

api/v2/zz_generated.deepcopy.go

Lines changed: 22 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/apisix.apache.org_apisixpluginconfigs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ spec:
7474
- plugins
7575
type: object
7676
status:
77-
description: ApisixPluginConfigStatus defines the observed state of ApisixPluginConfig.
77+
description: ApisixStatus is the status report for Apisix ingress Resources
7878
properties:
7979
conditions:
8080
items:

config/crd/bases/apisix.apache.org_apisixroutes.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ spec:
130130
required:
131131
- serviceName
132132
- servicePort
133-
- weight
134133
type: object
135134
type: array
136135
match:
@@ -193,9 +192,7 @@ spec:
193192
type: string
194193
required:
195194
- op
196-
- set
197195
- subject
198-
- value
199196
type: object
200197
type: array
201198
filter_func:
@@ -303,7 +300,6 @@ spec:
303300
type: boolean
304301
required:
305302
- name
306-
- websocket
307303
type: object
308304
type: array
309305
ingressClassName:

0 commit comments

Comments
 (0)