Skip to content

Commit

Permalink
Add LoadBalancerClass support in EventListener
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasbernard committed Apr 4, 2024
1 parent 9b47289 commit c154771
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 5 deletions.
9 changes: 9 additions & 0 deletions docs/eventlisteners.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,15 @@ spec:
servicePort: 8128
```
If you use a loadbalancer service, you can optionally define a (LoadBalancerClass)[https://kubernetes.io/docs/concepts/services-networking/service/#load-balancer-class] with the `ServiceLoadBalancerClass` attribute.
```yaml
spec:
resources:
kubernetesResource:
serviceType: LoadBalancer
serviceLoadBalancerClass: internal
```

#### Specifying `Replicas`

You can optionally use the `replicas` field to instruct Tekton Triggers to deploy more than one instance of your `EventListener` in individual Kubernetes Pods.
Expand Down
12 changes: 12 additions & 0 deletions docs/triggers-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3852,6 +3852,18 @@ int32
</tr>
<tr>
<td>
<code>serviceLoadBalancerClass</code><br/>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>Define the .spec.loadBalancerClass for Service with the type LoadBalancer.</p>
</td>
</tr>
<tr>
<td>
<code>spec</code><br/>
<em>
<a href="https://pkg.go.dev/knative.dev/pkg/apis/duck/v1#WithPodSpec">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
name: listener-loadbalancerclass
spec:
serviceAccountName: tekton-triggers-example-sa
triggers:
- name: foo-trig
bindings:
- ref: pipeline-binding
- ref: message-binding
template:
ref: pipeline-template
resources:
kubernetesResource:
serviceType: LoadBalancer
serviceLoadBalancerClass: private
9 changes: 5 additions & 4 deletions pkg/apis/triggers/v1beta1/event_listener_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,11 @@ type CustomResource struct {
}

type KubernetesResource struct {
Replicas *int32 `json:"replicas,omitempty"`
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
ServicePort *int32 `json:"servicePort,omitempty"`
duckv1.WithPodSpec `json:"spec,omitempty"`
Replicas *int32 `json:"replicas,omitempty"`
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
ServicePort *int32 `json:"servicePort,omitempty"`
ServiceLoadBalancerClass *string `json:"serviceLoadBalancerClass,omitempty"`
duckv1.WithPodSpec `json:"spec,omitempty"`
}

// EventListenerTrigger represents a connection between TriggerBinding, Params,
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/triggers/v1beta1/event_listener_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ func validateKubernetesObject(orig *KubernetesResource) (errs *apis.FieldError)
errs = errs.Also(validateEnv(orig.Template.Spec.Containers[0].Env).ViaField("spec.template.spec.containers[0].env"))
}

if orig.ServiceLoadBalancerClass != nil && orig.ServiceType != corev1.ServiceTypeLoadBalancer {
errs = errs.Also(apis.ErrInvalidValue(*orig.ServiceLoadBalancerClass, "serviceLoadBalancerClass", "ServiceLoadBalancerClass is only needed for LoadBalancer service type"))
}

return errs
}

Expand Down
36 changes: 36 additions & 0 deletions pkg/apis/triggers/v1beta1/event_listener_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,22 @@ func Test_EventListenerValidate(t *testing.T) {
},
},
},
}, {
name: "Valid EventListener with loadBalancerClass",
el: &triggersv1beta1.EventListener{
ObjectMeta: myObjectMeta,
Spec: triggersv1beta1.EventListenerSpec{
Triggers: []triggersv1beta1.EventListenerTrigger{{
TriggerRef: "triggerref",
}},
Resources: triggersv1beta1.Resources{
KubernetesResource: &triggersv1beta1.KubernetesResource{
ServiceType: corev1.ServiceTypeLoadBalancer,
ServiceLoadBalancerClass: ptr.String("lbc"),
},
},
},
},
}, {
name: "valid EventListener with embedded TriggerTemplate",
ctx: ctxWithAlphaFieldsEnabled,
Expand Down Expand Up @@ -1210,6 +1226,26 @@ func TestEventListenerValidate_error(t *testing.T) {
},
},
wantErr: apis.ErrMultipleOneOf("spec.resources.customResource", "spec.resources.kubernetesResource"),
}, {
name: "user specify a loadbalancerclass for a non-lb service type",
el: &triggersv1beta1.EventListener{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "namespace",
},
Spec: triggersv1beta1.EventListenerSpec{
Triggers: []triggersv1beta1.EventListenerTrigger{{
TriggerRef: "triggerref",
}},
Resources: triggersv1beta1.Resources{
KubernetesResource: &triggersv1beta1.KubernetesResource{
ServiceType: corev1.ServiceTypeNodePort,
ServiceLoadBalancerClass: ptr.String("sentinel"),
},
},
},
},
wantErr: apis.ErrInvalidValue("sentinel", "spec.resources.kubernetesResource.serviceLoadBalancerClass", "ServiceLoadBalancerClass is only needed for LoadBalancer service type"),
}, {
name: "user specify multiple containers, unsupported podspec and container field in custom resources",
el: &triggersv1beta1.EventListener{
Expand Down
6 changes: 6 additions & 0 deletions pkg/apis/triggers/v1beta1/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pkg/apis/triggers/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions pkg/reconciler/eventlistener/resources/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/ptr"
)

// makeEL is a helper to build an EventListener for tests.
Expand Down Expand Up @@ -94,6 +95,13 @@ func withServiceTypeLoadBalancer(el *v1beta1.EventListener) {
}
}

func withServiceTypeLoadBalancerClass(el *v1beta1.EventListener) {
el.Spec.Resources.KubernetesResource = &v1beta1.KubernetesResource{
ServiceType: "LoadBalancer",
ServiceLoadBalancerClass: ptr.String("lbc"),
}
}

func withServicePort80(el *v1beta1.EventListener) {
port := int32(80)
el.Spec.Resources.KubernetesResource = &v1beta1.KubernetesResource{
Expand Down
8 changes: 7 additions & 1 deletion pkg/reconciler/eventlistener/resources/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,19 @@ func MakeService(ctx context.Context, el *v1beta1.EventListener, c Config) *core

servicePort = ServicePort(el, c)

return &corev1.Service{
svc := &corev1.Service{
ObjectMeta: ObjectMeta(el, FilterLabels(ctx, el.Labels), c.StaticResourceLabels),
Spec: corev1.ServiceSpec{
Selector: GenerateLabels(el.Name, c.StaticResourceLabels),
Type: serviceType,
Ports: []corev1.ServicePort{servicePort, metricsPort}},
}

if el.Spec.Resources.KubernetesResource != nil && el.Spec.Resources.KubernetesResource.ServiceLoadBalancerClass != nil {
svc.Spec.LoadBalancerClass = el.Spec.Resources.KubernetesResource.ServiceLoadBalancerClass
}

return svc
}

func ServicePort(el *v1beta1.EventListener, c Config) corev1.ServicePort {
Expand Down
32 changes: 32 additions & 0 deletions pkg/reconciler/eventlistener/resources/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/kmeta"
"knative.dev/pkg/ptr"
)

func TestService(t *testing.T) {
Expand Down Expand Up @@ -97,6 +98,37 @@ func TestService(t *testing.T) {
},
},
}}, {
name: "EventListener with LoadBalancerClass",
el: makeEL(withServiceTypeLoadBalancerClass, withStatus),
want: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: generatedResourceName,
Namespace: namespace,
Labels: map[string]string{
"app.kubernetes.io/managed-by": "EventListener",
"app.kubernetes.io/part-of": "Triggers",
"eventlistener": eventListenerName,
},
OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(makeEL())},
},
Spec: corev1.ServiceSpec{
Type: "LoadBalancer",
LoadBalancerClass: ptr.String("lbc"),
Ports: []corev1.ServicePort{{
Name: eventListenerServicePortName,
Protocol: corev1.ProtocolTCP,
Port: int32(*config.Port),
TargetPort: intstr.IntOrString{
IntVal: int32(eventListenerContainerPort),
},
}, metricsPort},
Selector: map[string]string{
"app.kubernetes.io/managed-by": "EventListener",
"app.kubernetes.io/part-of": "Triggers",
"eventlistener": eventListenerName,
},
},
}}, {
name: "EventListener with service port: 80",
el: makeEL(withServicePort80, withStatus),
want: &corev1.Service{
Expand Down

0 comments on commit c154771

Please sign in to comment.