Skip to content

Commit ba03d4d

Browse files
authored
*: Implement union discriminator pattern in Backend API (#10709)
Signed-off-by: timflannagan <[email protected]>
1 parent eda5017 commit ba03d4d

File tree

10 files changed

+191
-64
lines changed

10 files changed

+191
-64
lines changed

api/applyconfiguration/api/v1alpha1/backendspec.go

+22-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/applyconfiguration/internal/internal.go

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/v1alpha1/backend_types.go

+57-13
Original file line numberDiff line numberDiff line change
@@ -22,40 +22,84 @@ type Backend struct {
2222
Status BackendStatus `json:"status,omitempty"`
2323
}
2424

25-
// +kubebuilder:object:root=true
26-
type BackendList struct {
27-
metav1.TypeMeta `json:",inline"`
28-
metav1.ListMeta `json:"metadata,omitempty"`
29-
Items []Backend `json:"items"`
30-
}
25+
// BackendType indicates the type of the backend.
26+
type BackendType string
3127

32-
// +kubebuilder:validation:XValidation:message="There must one and only one backend type set",rule="(has(self.aws) && !has(self.static) && !has(self.ai)) || (!has(self.aws) && has(self.static) && !has(self.ai)) || (!has(self.aws) && !has(self.static) && has(self.ai))"
33-
// +kubebuilder:validation:MaxProperties=1
34-
// +kubebuilder:validation:MinProperties=1
28+
const (
29+
// BackendTypeAI is the type for AI backends.
30+
BackendTypeAI BackendType = "ai"
31+
// BackendTypeAWS is the type for AWS backends.
32+
BackendTypeAWS BackendType = "aws"
33+
// BackendTypeStatic is the type for static backends.
34+
BackendTypeStatic BackendType = "static"
35+
)
36+
37+
// BackendSpec defines the desired state of Backend.
38+
// +union
39+
// +kubebuilder:validation:XValidation:message="ai backend must be nil if the type is not 'ai'",rule="!(has(self.ai) && self.type != 'ai')"
40+
// +kubebuilder:validation:XValidation:message="ai backend must be specified when type is 'ai'",rule="!(!has(self.ai) && self.type == 'ai')"
41+
// +kubebuilder:validation:XValidation:message="aws backend must be nil if the type is not 'aws'",rule="!(has(self.aws) && self.type != 'aws')"
42+
// +kubebuilder:validation:XValidation:message="aws backend must be specified when type is 'aws'",rule="!(!has(self.aws) && self.type == 'aws')"
43+
// +kubebuilder:validation:XValidation:message="static backend must be nil if the type is not 'static'",rule="!(has(self.static) && self.type != 'static')"
44+
// +kubebuilder:validation:XValidation:message="static backend must be specified when type is 'static'",rule="!(!has(self.static) && self.type == 'static')"
3545
type BackendSpec struct {
36-
Aws *AwsBackend `json:"aws,omitempty"`
46+
// Type indicates the type of the backend to be used.
47+
// +unionDiscriminator
48+
// +kubebuilder:validation:Enum=ai;aws;static
49+
// +kubebuilder:validation:Required
50+
Type BackendType `json:"type"`
51+
// AI is the AI backend configuration.
52+
// +optional
53+
AI *AIBackend `json:"ai,omitempty"`
54+
// Aws is the AWS backend configuration.
55+
// +optional
56+
Aws *AwsBackend `json:"aws,omitempty"`
57+
// Static is the static backend configuration.
58+
// +optional
3759
Static *StaticBackend `json:"static,omitempty"`
38-
AI *AIBackend `json:"ai,omitempty"`
3960
}
61+
62+
// AwsBackend is the AWS backend configuration.
4063
type AwsBackend struct {
41-
Region string `json:"region,omitempty"`
64+
// Region is the AWS region.
65+
// +optional
66+
Region string `json:"region,omitempty"`
67+
// SecretRef is the secret reference for the AWS credentials.
68+
// +optional
4269
SecretRef corev1.LocalObjectReference `json:"secretRef,omitempty"`
4370
}
71+
72+
// StaticBackend is the static backend configuration.
4473
type StaticBackend struct {
74+
// Hosts is the list of hosts.
75+
// +optional
76+
// +kubebuilder:validation:MinItems=1
4577
Hosts []Host `json:"hosts,omitempty"`
4678
}
4779

80+
// Host is a host and port pair.
4881
type Host struct {
82+
// Host is the host name.
4983
// +kubebuilder:validation:MinLength=1
5084
// +kubebuilder:validation:MaxLength=253
51-
Host string `json:"host"`
85+
Host string `json:"host"`
86+
// Port is the port number.
5287
Port gwv1.PortNumber `json:"port"`
5388
}
5489

90+
// BackendStatus defines the observed state of Backend.
5591
type BackendStatus struct {
92+
// Conditions is the list of conditions for the backend.
5693
// +optional
5794
// +listType=map
5895
// +listMapKey=type
5996
// +kubebuilder:validation:MaxItems=8
6097
Conditions []metav1.Condition `json:"conditions,omitempty"`
6198
}
99+
100+
// +kubebuilder:object:root=true
101+
type BackendList struct {
102+
metav1.TypeMeta `json:",inline"`
103+
metav1.ListMeta `json:"metadata,omitempty"`
104+
Items []Backend `json:"items"`
105+
}

api/v1alpha1/zz_generated.deepcopy.go

+5-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

install/helm/kgateway/crds/gateway.kgateway.dev_backends.yaml

+21-6
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ spec:
3232
metadata:
3333
type: object
3434
spec:
35-
maxProperties: 1
36-
minProperties: 1
3735
properties:
3836
ai:
3937
maxProperties: 1
@@ -484,14 +482,31 @@ spec:
484482
- host
485483
- port
486484
type: object
485+
minItems: 1
487486
type: array
488487
type: object
488+
type:
489+
enum:
490+
- ai
491+
- aws
492+
- static
493+
type: string
494+
required:
495+
- type
489496
type: object
490497
x-kubernetes-validations:
491-
- message: There must one and only one backend type set
492-
rule: (has(self.aws) && !has(self.static) && !has(self.ai)) || (!has(self.aws)
493-
&& has(self.static) && !has(self.ai)) || (!has(self.aws) && !has(self.static)
494-
&& has(self.ai))
498+
- message: ai backend must be nil if the type is not 'ai'
499+
rule: '!(has(self.ai) && self.type != ''ai'')'
500+
- message: ai backend must be specified when type is 'ai'
501+
rule: '!(!has(self.ai) && self.type == ''ai'')'
502+
- message: aws backend must be nil if the type is not 'aws'
503+
rule: '!(has(self.aws) && self.type != ''aws'')'
504+
- message: aws backend must be specified when type is 'aws'
505+
rule: '!(!has(self.aws) && self.type == ''aws'')'
506+
- message: static backend must be nil if the type is not 'static'
507+
rule: '!(has(self.static) && self.type != ''static'')'
508+
- message: static backend must be specified when type is 'static'
509+
rule: '!(!has(self.static) && self.type == ''static'')'
495510
status:
496511
properties:
497512
conditions:

internal/kgateway/extensions2/plugins/backend/plugin.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const (
3535
ParameterKind = "Parameter"
3636
)
3737
const (
38-
ExtensionName = "Upstream"
38+
ExtensionName = "Backend"
3939
FilterName = "io.solo.aws_lambda"
4040
)
4141

@@ -138,7 +138,7 @@ func NewPlugin(ctx context.Context, commoncol *common.CommonCollections) extensi
138138
},
139139
ContributesPolicies: map[schema.GroupKind]extensionsplug.PolicyPlugin{
140140
ParameterGK: {
141-
Name: "upstream",
141+
Name: "backend",
142142
NewGatewayTranslationPass: newPlug,
143143
// AttachmentPoints: []ir.AttachmentPoints{ir.HttpBackendRefAttachmentPoint},
144144
PoliciesFetch: func(n, ns string) ir.PolicyIR {
@@ -187,16 +187,18 @@ func processUpstream(ctx context.Context, in ir.BackendObjectIR, out *envoy_conf
187187
}
188188

189189
spec := up.Spec
190-
191190
switch {
192-
case spec.Static != nil:
191+
case spec.Type == v1alpha1.BackendTypeStatic:
193192
processStatic(ctx, spec.Static, out)
194-
case spec.Aws != nil:
193+
case spec.Type == v1alpha1.BackendTypeAWS:
195194
processAws(ctx, spec.Aws, ir, out)
196195
}
197196
}
198197

199198
func hostname(in *v1alpha1.Backend) string {
199+
if in.Spec.Type != v1alpha1.BackendTypeStatic {
200+
return ""
201+
}
200202
if in.Spec.Static != nil {
201203
if len(in.Spec.Static.Hosts) > 0 {
202204
return string(in.Spec.Static.Hosts[0].Host)
@@ -208,9 +210,9 @@ func hostname(in *v1alpha1.Backend) string {
208210
func processEndpoints(up *v1alpha1.Backend) *ir.EndpointsForBackend {
209211
spec := up.Spec
210212
switch {
211-
case spec.Static != nil:
213+
case spec.Type == v1alpha1.BackendTypeStatic:
212214
return processEndpointsStatic(spec.Static)
213-
case spec.Aws != nil:
215+
case spec.Type == v1alpha1.BackendTypeAWS:
214216
return processEndpointsAws(spec.Aws)
215217
}
216218
return nil
@@ -221,7 +223,7 @@ func newPlug(ctx context.Context, tctx ir.GwTranslationCtx) ir.ProxyTranslationP
221223
}
222224

223225
func (p *backendPlugin) Name() string {
224-
return "upstream"
226+
return ExtensionName
225227
}
226228

227229
// called 1 time for each listener

internal/kgateway/setup/testdata/accesslog-filtercel-httplisteneropt.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ metadata:
8181
name: log
8282
namespace: gwtest
8383
spec:
84+
type: static
8485
static:
8586
hosts:
8687
- host: 1.2.3.4

internal/kgateway/setup/testdata/backend.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ metadata:
3535
name: static
3636
namespace: gwtest
3737
spec:
38+
type: static
3839
static:
3940
hosts:
4041
- host: 1.2.3.4

internal/kgateway/translator/gateway/testutils/inputs/http-with-lambda-destination/manifests.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ kind: Backend
3838
metadata:
3939
name: aws-backend
4040
spec:
41+
type: aws
4142
aws:
4243
region: us-east-1
4344
# TODO: add these back
4445
# roleArn: arn:aws:iam::123456789012:role/lambda-role
4546
# lambdaFunctions:
4647
# - lambdaFunctionName: uppercase
47-
# logicalName: uppercase
48+
# logicalName: uppercase

0 commit comments

Comments
 (0)