830 lines
21 KiB
Go
830 lines
21 KiB
Go
/*
|
|
Copyright 2022 The Flux authors
|
|
|
|
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 server
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/google/go-github/v53/github"
|
|
"github.com/onsi/gomega"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
|
|
|
"github.com/fluxcd/pkg/apis/meta"
|
|
"github.com/fluxcd/pkg/runtime/logger"
|
|
|
|
apiv1 "github.com/fluxcd/notification-controller/api/v1"
|
|
)
|
|
|
|
func Test_handlePayload(t *testing.T) {
|
|
type hashOpts struct {
|
|
calculate bool
|
|
header string
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
hashOpts hashOpts
|
|
headers map[string]string
|
|
payload map[string]interface{}
|
|
receiver *apiv1.Receiver
|
|
receiverType string
|
|
secret *corev1.Secret
|
|
resources []client.Object
|
|
expectedResourcesAnnotated int
|
|
expectedResponseCode int
|
|
}{
|
|
{
|
|
name: "Generic receiver",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GenericReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "gitlab receiver",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "gitlab-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GitLabReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
headers: map[string]string{
|
|
"X-Gitlab-Token": "token",
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "cdevents receiver",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "cdevents-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.CDEventsReceiver,
|
|
Events: []string{"cd.change.merged.v1"},
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
headers: map[string]string{
|
|
"Ce-Type": "cd.change.merged.v1",
|
|
},
|
|
payload: map[string]interface{}{
|
|
"context": map[string]string{
|
|
"gitRepository": "adamkenihan/notification-controller",
|
|
"gitRevision": "5555",
|
|
"version": "0.3.0",
|
|
"id": "5555",
|
|
"source": "github",
|
|
"timestamp": "2023-12-07T14:51:29.908479495Z",
|
|
"type": "dev.cdevents.change.merged.0.1.2",
|
|
},
|
|
"subject": map[string]string{
|
|
"type": "change",
|
|
"id": "5555",
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "cdevents receiver wrong event type",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "cdevents-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.CDEventsReceiver,
|
|
Events: []string{"cd.environment.modified.v1"},
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
headers: map[string]string{
|
|
"Ce-Type": "cd.change.merged.v1",
|
|
},
|
|
payload: map[string]interface{}{
|
|
"context": map[string]string{
|
|
"gitRepository": "adamkenihan/notification-controller",
|
|
"gitRevision": "5555",
|
|
"version": "0.3.0",
|
|
"id": "5555",
|
|
"source": "github",
|
|
"timestamp": "2023-12-07T14:51:29.908479495Z",
|
|
"type": "dev.cdevents.change.merged.0.1.2",
|
|
},
|
|
"subject": map[string]string{
|
|
"type": "change",
|
|
"id": "5555",
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusBadRequest,
|
|
},
|
|
{
|
|
name: "cdevents receiver no event type specified",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "cdevents-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.CDEventsReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
headers: map[string]string{
|
|
"Ce-Type": "cd.change.merged.v1",
|
|
},
|
|
payload: map[string]interface{}{
|
|
"context": map[string]string{
|
|
"gitRepository": "adamkenihan/notification-controller",
|
|
"gitRevision": "5555",
|
|
"version": "0.3.0",
|
|
"id": "5555",
|
|
"source": "github",
|
|
"timestamp": "2023-12-07T14:51:29.908479495Z",
|
|
"type": "dev.cdevents.change.merged.0.1.2",
|
|
},
|
|
"subject": map[string]string{
|
|
"type": "change",
|
|
"id": "5555",
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "github receiver",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GitHubReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
hashOpts: hashOpts{
|
|
calculate: true,
|
|
header: github.SHA256SignatureHeader,
|
|
},
|
|
headers: map[string]string{
|
|
"Content-Type": "application/json",
|
|
},
|
|
payload: map[string]interface{}{
|
|
"action": "push",
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "generic hmac receiver",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "generic-hmac-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GenericHMACReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
hashOpts: hashOpts{
|
|
calculate: true,
|
|
header: "X-Signature",
|
|
},
|
|
headers: map[string]string{
|
|
"Content-Type": "application/json",
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "bitbucket receiver",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "bitbucket-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.BitbucketReceiver,
|
|
Events: []string{"push"},
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
hashOpts: hashOpts{
|
|
calculate: true,
|
|
header: github.SHA256SignatureHeader,
|
|
},
|
|
headers: map[string]string{
|
|
"Content-Type": "application/json",
|
|
"X-Event-Key": "push",
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "quay receiver",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "quay-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.QuayReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
payload: map[string]interface{}{
|
|
"docker_url": "docker.io",
|
|
"updated_tags": []string{
|
|
"v0.0.1",
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "harbor receiver",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "harbor-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.HarborReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
headers: map[string]string{
|
|
"Authorization": "token",
|
|
},
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "missing secret",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "missing-secret",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GenericReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "non-existing",
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusBadRequest,
|
|
},
|
|
{
|
|
name: "no receiver configured",
|
|
expectedResponseCode: http.StatusNotFound,
|
|
},
|
|
{
|
|
name: "not ready receiver is ignored",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "notready-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.StalledCondition, Status: metav1.ConditionFalse}},
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusServiceUnavailable,
|
|
},
|
|
{
|
|
name: "suspended receiver ignored",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "suspended-receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Suspend: true,
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusServiceUnavailable,
|
|
},
|
|
{
|
|
name: "missing apiVersion in resource",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GenericReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
Resources: []apiv1.CrossNamespaceObjectReference{
|
|
{
|
|
Kind: apiv1.ReceiverKind,
|
|
MatchLabels: map[string]string{
|
|
"label": "match",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusInternalServerError,
|
|
},
|
|
{
|
|
name: "resource by name not found",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GenericReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
Resources: []apiv1.CrossNamespaceObjectReference{
|
|
{
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
Kind: apiv1.ReceiverKind,
|
|
Name: "does-not-exists",
|
|
},
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusInternalServerError,
|
|
},
|
|
{
|
|
name: "annotating resources by label match",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GenericReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
Resources: []apiv1.CrossNamespaceObjectReference{
|
|
{
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
Kind: apiv1.ReceiverKind,
|
|
Name: "*",
|
|
MatchLabels: map[string]string{
|
|
"label": "match",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
resources: []client.Object{
|
|
&apiv1.Receiver{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: apiv1.ReceiverKind,
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "dummy-resource-2",
|
|
Labels: map[string]string{
|
|
"label": "does-not-match",
|
|
},
|
|
},
|
|
},
|
|
&apiv1.Receiver{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: apiv1.ReceiverKind,
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "dummy-resource",
|
|
Labels: map[string]string{
|
|
"label": "match",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expectedResourcesAnnotated: 1,
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "annotating resource by name",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GenericReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
Resources: []apiv1.CrossNamespaceObjectReference{
|
|
{
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
Kind: apiv1.ReceiverKind,
|
|
Name: "dummy-resource",
|
|
},
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
resources: []client.Object{
|
|
&apiv1.Receiver{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: apiv1.ReceiverKind,
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "dummy-resource-2",
|
|
},
|
|
},
|
|
&apiv1.Receiver{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: apiv1.ReceiverKind,
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "dummy-resource",
|
|
},
|
|
},
|
|
},
|
|
expectedResourcesAnnotated: 1,
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
{
|
|
name: "annotating all resources if name is *",
|
|
receiver: &apiv1.Receiver{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: apiv1.ReceiverKind,
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GenericReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
Resources: []apiv1.CrossNamespaceObjectReference{
|
|
{
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
Kind: apiv1.ReceiverKind,
|
|
Name: "*",
|
|
},
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
expectedResponseCode: http.StatusInternalServerError,
|
|
},
|
|
{
|
|
name: "resource matchLabels is ignored if name is not *",
|
|
receiver: &apiv1.Receiver{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "receiver",
|
|
},
|
|
Spec: apiv1.ReceiverSpec{
|
|
Type: apiv1.GenericReceiver,
|
|
SecretRef: meta.LocalObjectReference{
|
|
Name: "token",
|
|
},
|
|
Resources: []apiv1.CrossNamespaceObjectReference{
|
|
{
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
Kind: apiv1.ReceiverKind,
|
|
Name: "dummy-resource",
|
|
MatchLabels: map[string]string{
|
|
"label": "match",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Status: apiv1.ReceiverStatus{
|
|
WebhookPath: apiv1.ReceiverWebhookPath,
|
|
Conditions: []metav1.Condition{{Type: meta.ReadyCondition, Status: metav1.ConditionTrue}},
|
|
},
|
|
},
|
|
secret: &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "token",
|
|
},
|
|
Data: map[string][]byte{
|
|
"token": []byte("token"),
|
|
},
|
|
},
|
|
resources: []client.Object{
|
|
&apiv1.Receiver{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: apiv1.ReceiverKind,
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "dummy-resource-2",
|
|
},
|
|
},
|
|
&apiv1.Receiver{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: apiv1.ReceiverKind,
|
|
APIVersion: apiv1.GroupVersion.String(),
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "dummy-resource",
|
|
},
|
|
},
|
|
},
|
|
expectedResourcesAnnotated: 1,
|
|
expectedResponseCode: http.StatusOK,
|
|
},
|
|
}
|
|
|
|
scheme := runtime.NewScheme()
|
|
apiv1.AddToScheme(scheme)
|
|
corev1.AddToScheme(scheme)
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
g := gomega.NewGomegaWithT(t)
|
|
|
|
builder := fake.NewClientBuilder()
|
|
builder.WithScheme(scheme)
|
|
|
|
if tt.receiver != nil {
|
|
builder.WithObjects(tt.receiver)
|
|
}
|
|
|
|
builder.WithObjects(tt.resources...)
|
|
builder.WithIndex(&apiv1.Receiver{}, WebhookPathIndexKey, IndexReceiverWebhookPath)
|
|
|
|
if tt.secret != nil {
|
|
builder.WithObjects(tt.secret)
|
|
}
|
|
|
|
client := builder.Build()
|
|
s := ReceiverServer{
|
|
port: "",
|
|
logger: logger.NewLogger(logger.Options{}),
|
|
kubeClient: client,
|
|
}
|
|
|
|
data, err := json.Marshal(tt.payload)
|
|
if err != nil {
|
|
t.Errorf("error marshalling test payload: '%s'", err)
|
|
}
|
|
req := httptest.NewRequest("POST", "/hook/", bytes.NewBuffer(data))
|
|
for key, val := range tt.headers {
|
|
req.Header.Set(key, val)
|
|
}
|
|
if tt.hashOpts.calculate {
|
|
mac := hmac.New(sha256.New, tt.secret.Data["token"])
|
|
_, err := mac.Write(data)
|
|
if err != nil {
|
|
t.Errorf("error writing hmac: '%s'", err)
|
|
}
|
|
req.Header.Set(tt.hashOpts.header, "sha256="+hex.EncodeToString(mac.Sum(nil)))
|
|
}
|
|
|
|
rr := httptest.NewRecorder()
|
|
handler := s.handlePayload()
|
|
handler(rr, req)
|
|
g.Expect(rr.Result().StatusCode).To(gomega.Equal(tt.expectedResponseCode))
|
|
|
|
var allReceivers apiv1.ReceiverList
|
|
g.Expect(client.List(context.TODO(), &allReceivers)).To(gomega.Succeed())
|
|
|
|
var annotatedResources int
|
|
for _, obj := range allReceivers.Items {
|
|
if _, ok := obj.GetAnnotations()[meta.ReconcileRequestAnnotation]; ok {
|
|
annotatedResources++
|
|
}
|
|
}
|
|
|
|
g.Expect(annotatedResources).To(gomega.Equal(tt.expectedResourcesAnnotated))
|
|
})
|
|
}
|
|
}
|