Skip to content

Commit

Permalink
Add reconciliation interval to providers and receivers
Browse files Browse the repository at this point in the history
Periodically reconcile providers and receivers with their
Secret references to surface config errors after initialisation.

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
  • Loading branch information
stefanprodan committed Nov 7, 2022
1 parent a8f8dfc commit 1896ae3
Show file tree
Hide file tree
Showing 13 changed files with 113 additions and 2 deletions.
7 changes: 7 additions & 0 deletions api/v1beta2/provider_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ type ProviderSpec struct {
// +required
Type string `json:"type"`

// Interval at which to reconcile the Provider with its Secret references.
// +kubebuilder:default="10m"
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +required
Interval metav1.Duration `json:"interval"`

// Channel specifies the destination channel where events should be posted.
// +kubebuilder:validation:MaxLength:=2048
// +optional
Expand Down
7 changes: 7 additions & 0 deletions api/v1beta2/receiver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ type ReceiverSpec struct {
// +required
Type string `json:"type"`

// Interval at which to reconcile the Receiver with its Secret references.
// +kubebuilder:default="10m"
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Pattern="^([0-9]+(\\.[0-9]+)?(ms|s|m|h))+$"
// +required
Interval metav1.Duration `json:"interval"`

// Events specifies the list of event types to handle,
// e.g. 'push' for GitHub or 'Push Hook' for GitLab.
// +optional
Expand Down
2 changes: 2 additions & 0 deletions api/v1beta2/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ spec:
should be posted.
maxLength: 2048
type: string
interval:
default: 10m
description: Interval at which to reconcile the Provider with its
Secret references.
pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$
type: string
proxy:
description: Proxy the HTTP/S address of the proxy server.
maxLength: 2048
Expand Down Expand Up @@ -302,6 +308,7 @@ spec:
maxLength: 2048
type: string
required:
- interval
- type
type: object
status:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ spec:
items:
type: string
type: array
interval:
default: 10m
description: Interval at which to reconcile the Receiver with its
Secret references.
pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$
type: string
resources:
description: A list of resources to be notified about changes.
items:
Expand Down Expand Up @@ -330,6 +336,7 @@ spec:
- acr
type: string
required:
- interval
- resources
- type
type: object
Expand Down
4 changes: 4 additions & 0 deletions controllers/alert_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func TestAlertReconciler_Reconcile(t *testing.T) {
g.Expect(k8sClient.Create(context.Background(), alert)).To(Succeed())

t.Run("fails with provider not found error", func(t *testing.T) {
g := NewWithT(t)
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(alert), resultA)
return conditions.Has(resultA, meta.ReadyCondition)
Expand All @@ -104,6 +105,7 @@ func TestAlertReconciler_Reconcile(t *testing.T) {
})

t.Run("recovers when provider exists", func(t *testing.T) {
g := NewWithT(t)
g.Expect(k8sClient.Create(context.Background(), provider)).To(Succeed())

g.Eventually(func() bool {
Expand All @@ -117,6 +119,7 @@ func TestAlertReconciler_Reconcile(t *testing.T) {
})

t.Run("handles reconcileAt", func(t *testing.T) {
g := NewWithT(t)
reconcileRequestAt := metav1.Now().String()
resultA.SetAnnotations(map[string]string{
meta.ReconcileRequestAnnotation: reconcileRequestAt,
Expand All @@ -130,6 +133,7 @@ func TestAlertReconciler_Reconcile(t *testing.T) {
})

t.Run("finalizes suspended object", func(t *testing.T) {
g := NewWithT(t)
resultA.Spec.Suspend = true
g.Expect(k8sClient.Update(context.Background(), resultA)).To(Succeed())

Expand Down
2 changes: 1 addition & 1 deletion controllers/provider_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func (r *ProviderReconciler) reconcile(ctx context.Context, obj *apiv1.Provider)
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, apiv1.InitializedReason)
ctrl.LoggerFrom(ctx).Info("Provider initialized")

return ctrl.Result{}, nil
return ctrl.Result{RequeueAfter: obj.Spec.Interval.Duration}, nil
}

func (r *ProviderReconciler) validate(ctx context.Context, provider *apiv1.Provider) error {
Expand Down
5 changes: 5 additions & 0 deletions controllers/provider_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func TestProviderReconciler_Reconcile(t *testing.T) {
g.Expect(k8sClient.Create(context.Background(), provider)).To(Succeed())

t.Run("reports ready status", func(t *testing.T) {
g := NewWithT(t)
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(provider), resultP)
return resultP.Status.ObservedGeneration == resultP.Generation
Expand All @@ -75,6 +76,7 @@ func TestProviderReconciler_Reconcile(t *testing.T) {
})

t.Run("fails with secret not found error", func(t *testing.T) {
g := NewWithT(t)
resultP.Spec.SecretRef = &meta.LocalObjectReference{
Name: secretName,
}
Expand All @@ -95,6 +97,7 @@ func TestProviderReconciler_Reconcile(t *testing.T) {
})

t.Run("recovers when secret exists", func(t *testing.T) {
g := NewWithT(t)
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Expand All @@ -117,6 +120,7 @@ func TestProviderReconciler_Reconcile(t *testing.T) {
})

t.Run("handles reconcileAt", func(t *testing.T) {
g := NewWithT(t)
reconcileRequestAt := metav1.Now().String()
resultP.SetAnnotations(map[string]string{
meta.ReconcileRequestAnnotation: reconcileRequestAt,
Expand All @@ -130,6 +134,7 @@ func TestProviderReconciler_Reconcile(t *testing.T) {
})

t.Run("finalizes suspended object", func(t *testing.T) {
g := NewWithT(t)
resultP.Spec.Suspend = true
g.Expect(k8sClient.Update(context.Background(), resultP)).To(Succeed())

Expand Down
2 changes: 1 addition & 1 deletion controllers/receiver_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func (r *ReceiverReconciler) reconcile(ctx context.Context, obj *apiv1.Receiver)

ctrl.LoggerFrom(ctx).Info(msg)

return ctrl.Result{}, nil
return ctrl.Result{RequeueAfter: obj.Spec.Interval.Duration}, nil
}

// patch updates the object status, conditions and finalizers.
Expand Down
8 changes: 8 additions & 0 deletions controllers/receiver_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func TestReceiverReconciler_Reconcile(t *testing.T) {
g.Expect(k8sClient.Create(context.Background(), receiver)).To(Succeed())

t.Run("reports ready status", func(t *testing.T) {
g := NewWithT(t)
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(receiver), resultR)
return resultR.Status.ObservedGeneration == resultR.Generation
Expand All @@ -102,6 +103,7 @@ func TestReceiverReconciler_Reconcile(t *testing.T) {
})

t.Run("fails with secret not found error", func(t *testing.T) {
g := NewWithT(t)
g.Expect(k8sClient.Delete(context.Background(), secret)).To(Succeed())

reconcileRequestAt := metav1.Now().String()
Expand All @@ -124,6 +126,7 @@ func TestReceiverReconciler_Reconcile(t *testing.T) {
})

t.Run("recovers when secret exists", func(t *testing.T) {
g := NewWithT(t)
newSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Expand All @@ -146,6 +149,7 @@ func TestReceiverReconciler_Reconcile(t *testing.T) {
})

t.Run("handles reconcileAt", func(t *testing.T) {
g := NewWithT(t)
reconcileRequestAt := metav1.Now().String()
resultR.SetAnnotations(map[string]string{
meta.ReconcileRequestAnnotation: reconcileRequestAt,
Expand All @@ -159,6 +163,7 @@ func TestReceiverReconciler_Reconcile(t *testing.T) {
})

t.Run("finalizes suspended object", func(t *testing.T) {
g := NewWithT(t)
resultR.Spec.Suspend = true
g.Expect(k8sClient.Update(context.Background(), resultR)).To(Succeed())

Expand Down Expand Up @@ -254,6 +259,7 @@ func TestReceiverReconciler_EventHandler(t *testing.T) {
address := fmt.Sprintf("/hook/%s", sha256sum(token+receiverKey.Name+receiverKey.Namespace))

t.Run("generates URL when ready", func(t *testing.T) {
g := NewWithT(t)
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(receiver), resultR)
return conditions.IsReady(resultR)
Expand All @@ -263,6 +269,7 @@ func TestReceiverReconciler_EventHandler(t *testing.T) {
})

t.Run("doesn't update the URL on spec updates", func(t *testing.T) {
g := NewWithT(t)
resultR.Spec.Events = []string{"ping", "push"}
g.Expect(k8sClient.Update(context.Background(), resultR)).To(Succeed())

Expand All @@ -276,6 +283,7 @@ func TestReceiverReconciler_EventHandler(t *testing.T) {
})

t.Run("handles event", func(t *testing.T) {
g := NewWithT(t)
res, err := http.Post("http://localhost:56788/"+address, "application/json", nil)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(res.StatusCode).To(Equal(http.StatusOK))
Expand Down
52 changes: 52 additions & 0 deletions docs/api/notification.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,19 @@ string
</tr>
<tr>
<td>
<code>interval</code><br>
<em>
<a href="https://godoc.org/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">
Kubernetes meta/v1.Duration
</a>
</em>
</td>
<td>
<p>Interval at which to reconcile the Provider with its Secret references.</p>
</td>
</tr>
<tr>
<td>
<code>channel</code><br>
<em>
string
Expand Down Expand Up @@ -432,6 +445,19 @@ the validation procedure and payload deserialization.</p>
</tr>
<tr>
<td>
<code>interval</code><br>
<em>
<a href="https://godoc.org/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">
Kubernetes meta/v1.Duration
</a>
</em>
</td>
<td>
<p>Interval at which to reconcile the Receiver with its Secret references.</p>
</td>
</tr>
<tr>
<td>
<code>events</code><br>
<em>
[]string
Expand Down Expand Up @@ -776,6 +802,19 @@ string
</tr>
<tr>
<td>
<code>interval</code><br>
<em>
<a href="https://godoc.org/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">
Kubernetes meta/v1.Duration
</a>
</em>
</td>
<td>
<p>Interval at which to reconcile the Provider with its Secret references.</p>
</td>
</tr>
<tr>
<td>
<code>channel</code><br>
<em>
string
Expand Down Expand Up @@ -976,6 +1015,19 @@ the validation procedure and payload deserialization.</p>
</tr>
<tr>
<td>
<code>interval</code><br>
<em>
<a href="https://godoc.org/k8s.io/apimachinery/pkg/apis/meta/v1#Duration">
Kubernetes meta/v1.Duration
</a>
</em>
</td>
<td>
<p>Interval at which to reconcile the Receiver with its Secret references.</p>
</td>
</tr>
<tr>
<td>
<code>events</code><br>
<em>
[]string
Expand Down
6 changes: 6 additions & 0 deletions docs/spec/v1beta2/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@ stringData:

`.spec.proxy` is an optional field to specify an HTTP/S proxy address.

### Interval

`.spec.interval` is a required field with a default of ten minutes that specifies
the time interval at which the controller reconciles the provider with its Secret
references.

### Suspend

`.spec.suspend` is an optional field to suspend the provider.
Expand Down
6 changes: 6 additions & 0 deletions docs/spec/v1beta2/receivers.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ to another tenant's resources.
`.spec.secretRef.name` is a required field to specify a name reference to a
Secret in the same namespace as the Receiver, containing the secret token.

### Interval

`.spec.interval` is a required field with a default of ten minutes that specifies
the time interval at which the controller reconciles the provider with its Secret
references.

### Suspend

`.spec.suspend` is an optional field to suspend the receiver.
Expand Down

0 comments on commit 1896ae3

Please sign in to comment.